【题目描述】
对于给定的一个长度为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 }