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; }