数列分段II(信息学奥赛一本通 1436)(洛谷 1182)
【题目描述】
对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:
例如一数列4 2 4 5 1要分成3段
将其如下分段:
[4 2][4 5][1]
第一段和为6,第2段和为9,第3段和为1,和最大值为9。
将其如下分段:
[4][2 4][5 1]
第一段和为4,第2段和为6,第3段和为6,和最大值为6。
并且无论如何分段,最大值不会小于6。
所以可以得到要将数列4 2 4 5 1要分成3段,每段和的最大值最小为6。
【输入】
第1行包含两个正整数N,M,第2行包含N个空格隔开的非负整数A[i],含义如题目所述。
【输出】
仅包含一个正整数,即每段和最大值最小为多少。
【输入样例】
5 3 4 2 4 5 1
【输出样例】
6
最小值最大(或是最大值最小)问题,这类双最值问题常常选用二分法求解。本题就是一道典型的“二分答案”模型。
因为每段和最大值的取值区间是[ max(a[1],a[2],a[3]……a[n]) , sum(a[1],a[2],a[3]……a[n]) ],所以就二分l和r,令mid=l+r>>1,每次进行check,计算当mid为最大值的情况下可以分成的段数cnt,
-
若cnt>m,说明这个mid小了,则令l=mid+1;
-
若cnt<=m,说明这个mid大了,则令r=mid-1,由于此时cnt可能等于m,所以用ans记录下来;
详见代码——
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1e6+5; 4 int a[N]; 5 int n; 6 int m,q,w,ans,p,tot; 7 int read() 8 { 9 int f=1;char ch; 10 while((ch=getchar())<'0'||ch>'9') 11 if(ch=='-')f=-1; 12 int res=ch-'0'; 13 while((ch=getchar())>='0'&&ch<='9') 14 res=res*10+ch-'0'; 15 return res*f; 16 } 17 void write(int x) 18 { 19 if(x<0) 20 { 21 putchar('-'); 22 x=-x; 23 } 24 if(x>9)write(x/10); 25 putchar(x%10+'0'); 26 } 27 bool check(int s) 28 { 29 int cnt=1,now=0; 30 for(int i=1;i<=n;i++) 31 { 32 if(a[i]+now<=s)now+=a[i]; 33 else 34 { 35 cnt++; 36 now=a[i]; 37 } 38 } 39 return cnt<=m; 40 } 41 int main() 42 { 43 n=read();m=read();int ma=-1,sum=0; 44 for(int i=1;i<=n;i++) 45 { 46 a[i]=read(); 47 ma=max(ma,a[i]); 48 sum+=a[i]; 49 } 50 int l=ma,r=sum; 51 while(l<=r) 52 { 53 int mid=l+r>>1; 54 if(check(mid)) 55 { 56 ans=mid;r=mid-1; 57 } 58 else l=mid+1; 59 } 60 write(ans); 61 return 0; 62 }