NOIP第二次模拟赛 stage1【划分数列(seq.pas/c/cpp)
7划分数列(seq.pas/c/cpp)
【题目描述】
给你一个有n个元素的数列,要求把它划分成k段,使每段元素和的最大值最小
【输入格式】
第一行两个正整数n,k
第二行为此数列ai
【输出格式】
一行一个数,为题目所求答案
【样例输入】
5 2
2 1 3 4 5
【样例输出】
9
【数据规模】
30%数据 n <= 30, k <= 10
100%数据 n <= 100000, k <= n, ai <= 10^9
150%数据 n <= 100000, k <= n, |ai| <= 10^9(附:这50分超越了noip难度,大家可以无视)
【时限】
1s
刚拿到的时候没看数据觉得是动归,QAQ看了数据觉得应该要有优化,二分或者贪心吧。
然而考试的时候并没有做出来(╯‵□′)╯︵┻━┻后来看到大牛的题解,写得很好,贴在这里
http://blog.sina.com.cn/s/blog_9aa2786a01018ksx.html 量子压缩的新浪博客
标程果然二分,当然这道题最大的难点不在二分数据而在于判断是否能够划分这个数据;
下面代码中有足够的注释,文末贴出判断过程
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int n,k; 6 long long sum,maxn; 7 long long num[100001]; 8 void read() 9 { 10 scanf("%d%d",&n,&k); 11 for(int i=1;i<=n;i++) 12 { 13 scanf("%lld",&num[i]); 14 sum+=num[i];//记录总和; 15 maxn=max(maxn,num[i]);//记录最大值; 16 } 17 } 18 bool judge(int x)//判断能否划分这个数 19 { 20 long long temp=0; 21 int pos=0;//记录划分的段数; 22 for(int i=1;i<=n;i++) 23 { 24 temp+=num[i]; 25 if(temp<=x&&(temp+num[i+1])>x)//在i这个位置可以划分; 26 { 27 temp=0;//重置 28 pos++;//段数加一; 29 } 30 if(pos==k&&i<n) return 0;//如果划分的段数将要超过k,那么不能划分为k段,即这个数不能划分; 31 } 32 return 1;//否则能划分; 33 } 34 void work()//二分 35 { 36 long long r=sum; 37 long long l=maxn; 38 long long m; 39 while(l<=r) 40 { 41 long long mid=(l+r)/2; 42 if(judge(mid))//若这个数可以划分,向左寻找比它小的下一个可以划分的数; 43 { 44 r=mid-1; 45 m=mid;//记录这个可以划分的数; 46 } 47 else l=mid+1;//若不能划分,向右寻找比它大的下一个可以划分的数; 48 } 49 printf("%lld",m); 50 } 51 int main() 52 { 53 freopen("seq.in","r",stdin); 54 freopen("seq.out","w",stdout); 55 read(); 56 work(); 57 return 0; 58 }
下面的判断过程也许有点冗长(三个图),代码看懂的同学没有必要往下翻啦。
—!@¥~!#@W~……%——心情复杂的分割线lヾ(。`Д´。)——$—%……¥%¥#¥@#@@@!—
以样例为例,
第一次二分:l=5,r=15,mid=10;
judge
10可以划分,于是r=mid-1=9;
mid=(9+5)/2=7;
7不能划分(划分需要3段),所以l=8,mid=7,后面重复上述过程;
直到mid=9,
终于可以划分了(っ*´Д`)っ,此时m=9,r=8,l=8+1=9,因为l>r,所以二分完毕,打印答案。
前面十个数据没有负数,最后五个有_(:з」∠)_,现在还写不出来所以只得了100分没有AC这道题。
应该会有后续更新【╰(*°▽°*)╯