寫在前面:二分答案類型的題目有固定的套路,學會以後這類題就能迎刃而解。但要注意認真對待,記錄下曾踩過的坑,以免日後再犯(呵呵,將來一定還會錯)
題目描述
算法
(二分答案)
最大值的最小化問題
Max
15 52 33-> 52
10 50 30 -> 50
30 30 30 -> 30
二分模型:找到第一個滿足要求的數(右半段)
二分答案套路:先找出答案可能的區間,然後判斷答案是否滿足條件,根據條件對區間進行二分
注意:確定區間時如果精確的去求可能的範圍,那check函數不用加額外判斷;如果是隨意擴大了範圍,那x如果很小,小於num[i],那它必然不是最大值,所以此時必須直接返回false(我的check函數寫法的原因)
Debug:
- 一開始m沒有傳到check裏面,我以爲m是全就不用穿了,可是這樣會導致m的值在下一輪會改變。
- 做這個題還想到了101 和 1900 二分不是會有小數嗎,原來l + r + 1就是用來解決這個的。
- 一個月沒刷題了,有點忘了自頂向下做題步驟,求答案區間範圍時max居然寫成了min。其實求最大範圍時,直接設一個無窮大值即可
- 不管怎麼樣,check函數一開始一定要m–,即一定要用一天,然後如果超過x了,說明還要再用一天,這是二分答案的套路
Saturday, May 9, 2020 14:47:37
更新:建議還是求出區間的精確範圍,因爲check函數那個判斷條件很容易忘記寫
時間複雜度是,空間複雜度是
C++代碼
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 7;
int n, m;
LL a[N];
LL l = 0, r = 0;
bool check(int x, int m) {
LL sum = 0;
m --; // 一開始一定要用1天
// 2 4 | 3 2 | 3 (m = 7)
for (int i = 0; i < n; i ++) {
sum += a[i];
if (sum > x) { // 如果超過x,說明還要再用一天
sum = a[i];
m --;
if (a[i] > x) return false; // 如果隨意擴大了範圍,那這句話一定要寫
}
}
return m >= 0;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
cin >> n >> m;
// 找到答案可能的區間
for (int i = 0; i < n; i ++) {
cin >> a[i];
// r += a[i];
// l = max(l, a[i]);
}
l = -2e9, r = 2e9; // 用無窮大設範圍是可以的,但要注意check函數
while (l < r) {
LL mid = (l + r) >> 1;
if (check(mid, m)) r = mid;
else l = mid + 1;
}
cout << r << endl;
return 0;
}