该题是所谓“最大值尽量小”的典型代表,方法就是用二分猜这个最值,判断函数就是从前向后扫,尽量向后划,如果最后划出的组数比k小,那么显然可以划成k组。
代码如下:
#include<bits/stdc++.h> using namespace std; typedef long long ll; int T,n,k,a[505]; bool P(int m) { int ans=0,cnt=1; for(int i=0;i<n;i++) { ans+=a[i]; if(ans>m) { ans = a[i]; cnt++; } } if(cnt<=k) return true; else return false; } int main() { scanf("%d",&T); while(T--){ scanf("%d%d",&n,&k); ll l = 0,r = 0; for(int i=0;i<n;i++) { scanf("%d",&a[i]); r += a[i]; if(l<a[i]) l = a[i]; } ll m; while(r > l) { //二分查找最大和的最小可能 m = (l+r)/2; if(P(m)) r = m; else l = m+1; } int ans[505]; memset(ans,0,sizeof(ans)); //用来记录每一部分的数字个数 int cnt = 0; ll bbc = 0; for(int i=n-1;i>=0;i--){ bbc += a[i]; if(bbc>r){ bbc = a[i]; cnt++;//但是这样做是有缺陷的,比如本来应该是 1 1 2的可能变成 0 2 2 ans[cnt]++; } else ans[cnt]++; } cnt ++; int q = 0; for(int i=k-1;i>=0;i--){ //因此将为0的部分用后面的数补上 if(ans[i]!=0) break; else { for(int j=i-1;j>=0;j--){ if(ans[j]>1){ ans[j]--; ans[i]++; break; } } } } for(int i=k-1;i>=0;i--){ //打印 for(int j=0;j<ans[i];j++){ if(q==n-1) cout<<a[q++]; else cout<<a[q++]<<' '; } if(q!=n) cout<<"/ "; } cout<<"\n"; } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-20 01:20:25