题意:将1个含N个正整数的序列划分成K个连续的子序列,使每段的和的最大值尽量小,问字典序最小的划分方案。
解法:由于是连续的数的“最大值最小”,便可想到二分每段的最大值,若这时可分成<=K段,则这个最大值成立,再继续二分。
输出方案需要用到贪心策略, 先从后往前贪心求得最小划分的段数M,若M不足K,则直接使K-M个数单独划分为一段,保证字典序最小。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 const int N=510,D=(int)1e7+10;//N*D long long 7 typedef long long LL; 8 int a[N],v[N]; 9 int n,k; 10 11 int check(LL x) 12 { 13 LL t=1,h=0; 14 for (int i=1;i<=n;i++) 15 { 16 h+=a[i]; 17 if (h>x) h=a[i],t++; 18 } 19 return t<=k; 20 } 21 int main() 22 { 23 int T; 24 scanf("%d",&T); 25 while (T--) 26 { 27 long long h=0,mx=0; 28 scanf("%d%d",&n,&k); 29 for (int i=1;i<=n;i++) 30 scanf("%d",&a[i]),h+=a[i],mx=mx>a[i]?mx:a[i]; 31 LL l=mx,r=h,mid,ans=1<<30; 32 while (l<=r) 33 { 34 mid=(l+r)>>1; 35 int tmp=check(mid); 36 if (tmp) ans=mid,r=mid-1; 37 else l=mid+1; 38 } 39 int e=1; h=0; 40 for (int i=n;i>=1;i--) 41 { 42 h+=a[i], v[i]=0; 43 if (h>ans) h=a[i],e++,v[i]=1; 44 } 45 h=0; 46 for (int i=1;i<=n;i++) 47 { 48 printf("%d",a[i]); 49 if (v[i]) printf(" /"); 50 else if (h<k-e) printf(" /"),h++; 51 if (i<n) printf(" "); 52 } 53 printf(" "); 54 } 55 return 0; 56 }