题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=85904#problem/B
题意:
多组案例,大意是说先给定两个数m和k,m表示数据个数,k表示将m个数据分为k份,要求划分后的子序列的和的最大值最小,每次划分尽量往右划分。1<=k<=m<=500,子序列和的最大值不超过10000000。
输入:首行输入案例数,次行输入m和k(使用空格符分隔),第三行输入m个数据。
输出:输出m个数据并在分割处使用'/'间隔。
案例:
Sample Input
2
9 3
100 200 300 400 500 600 700 800 900
5 4
100 100 100 100 100
Sample Output
100 200 300 400 500 / 600 700 / 800 900
100 / 100 / 100 / 100 100
分析:
找子序列的和可以先从和的范围查找,容易判断该和的范围为原序列中最大的元素值至其整个序列的和,然后我们就可以利用二分法查找这个子序列的和的最大值的最小值了,查找划分最好从序列右端开始。
源代码:
1 #include<cstdio> 2 #include<cstring> 3 typedef long long ll;//声明定义ll表示long long型 4 ll T,m,k,a[510],b[510],ans[510],left,right,mid; 5 ll check(ll max) 6 { 7 ll j,num=0,tot=0; 8 memset(b,0,sizeof(b)); 9 for(j=m;j>=1;j--)//由后往前查找'/'可摆放处 10 { 11 if(tot+a[j]<=max&&j>=k-num)//判断a[j]处能否继续摆放'/' 12 tot+=a[j]; 13 else 14 { 15 ++num;//切割次数记录 16 tot=a[j]; 17 b[j]=1;//测试设定'/'摆放标志 18 } 19 } 20 if(num+1==k)//判断测试结果是否满足条件 21 { 22 for(j=1;j<=m;j++) 23 ans[j]=b[j]; 24 return 1; 25 } 26 else return 0; 27 } 28 int main() 29 { 30 ll i; 31 scanf("%lld",&T);//案例数 32 while(T--) 33 { 34 left=0; 35 right=0; 36 scanf("%lld%lld",&m,&k); 37 for(i=1;i<=m;i++) 38 { 39 scanf("%lld",&a[i]); 40 right+=a[i];//将序列的和设为子序列和的最大限值 41 if(a[i]>left) left=a[i];//将序列中最大的值设为子序列的最小限值 42 } 43 memset(ans,0,sizeof(ans)); 44 while(left<=right)//二分法查找最小的子序列的和 45 { 46 mid=(left+right)/2; 47 if(check(mid)==1) 48 right=mid-1; 49 else left=mid+1; 50 } 51 for(i=1;i<m;i++)//输出控制 52 { 53 if(ans[i]==1) 54 printf("%lld / ",a[i]); 55 else printf("%lld ",a[i]); 56 } 57 printf("%lld ",a[m]); 58 } 59 return 0; 60 }