LiberOJ 10014 数列分段 II 二分

题意

给定长度为 \(N\) 的序列 \(A\),要将其划分为连续的 \(M\) 段,并最小化每一段总和的最大值。

输入格式

第1行包含两个正整数 \(N,M\)

第2行包含 \(N\) 个空格隔开的非负整数 \(A_i\),含义如题目所述。

输出格式

仅包含一个正整数,即每段和最大值最小为多少。

Input

5 3
4 2 4 5 1

Output

6

\(Note:\) 划分方式可以为:\([4,2][4][5,1]\)

Solution

我们二分答案,答案的左端点 \(l = \max_i(A_i)\),右端点 \(r = \sum_i A_i\)
我们每次 \(check\) 答案的时候,统计段落数 \(cnt\).

如果 \(cnt\geq M\),说明我们此时需要增大左端点(因为上限偏小,这样才会使得段落数变多)

\(Code:\)

点击查看代码
int n,m; const int N = 1e5+5; int a[N]; int sum[N];  bool check(int x){     int cnt=0;     int cur = 0;     for(int i=2;i<=n;i++){         if(sum[i]-sum[cur]>x){             cur = i-1;             cnt+=1;         }     }     if(cnt>=m) return true;     return false; }  int main(){     //ios::sync_with_stdio(false);     n = read();m = read();     int l=0,r;     for(int i=1;i<=n;i++)a[i] = read(),sum[i] = sum[i-1]+a[i],l = max(l,a[i]);     r = sum[n];     int ans = sum[n];     while(l<r){         int mid = (l+r)>>1;         if(check(mid)){             l=mid+1;         }         else{r=mid;}     }     cout<<r<<endl;  } 

\(\\\)
这里的二分模板为:

点击查看代码
int bsearch_1(int l, int r) {     while (l < r)     {         int mid = l + r >> 1;         if (check(mid)) r = mid;         else l = mid + 1;     }     return l; }