该题是所谓“最大值尽量小”的典型代表,方法就是用二分猜这个最值,判断函数就是从前向后扫,尽量向后划,如果最后划出的组数比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;
}