二分查找最小的最大值,然后贪心划分区间
题解:x为划分的各个区间之和的最大值,那么x在[min,max]中,min为序列中的最小值(0也行,用最小值可以稍微减少二分的时间,),max为序列之和。
函数P(x)表示当子序列最大值为x时,能至少划分出的区间个数,即每次尽量向右划。如果划完整个序列之后区间数<=K,那么x一定大于等于答案,如果区间数大于K,那么x一定取偏大了。不同情况进行二分,直到找到最小的x,使得划分的区间数<=K(想一下为什么不是等于k,而是也有等于的时候)
找到最小的最大值ans后,由于题目中要求有多解是前面的区间尽量小,所以我们从右边开始划分,每次尽量往左划。
最后将没用到的‘/’放到最靠右边能分隔的位置。
注意使用 long long
#include<bits/stdc++.h>
using namespace std;
long long a[505],m,k;
set<int>ans;
long long Min,Max;
bool P(long long x){
long long sum=0,flag=0;
for(int i=0;i<m;i++){
if(sum+a[i]<=x){sum+=a[i];}
else{
flag++;
if(flag>=k) return false;
sum=a[i];
}
}
return true;
}
int solve(long long l,long long r){
long long mid=(l+r)/2;
if(l==r) return l;
if(P(mid)){
return solve(l,mid);
}
else{
return solve(mid+1,r);
}
}
int main()
{
int t;
cin>>t;
while(t--){
Max=Min=0;
ans.clear();
cin>>m>>k;
for(int i=0;i<m;i++){
scanf("%d",&a[i]);
Min=max(Min,a[i]);
Max+=a[i];
}
int p=solve(Min,Max);
int sum=0,flag=0;
for(int i=m-1;i>=0;i--){
if(sum+a[i]<=p) {sum+=a[i];}
else{
ans.insert(i+1);
flag++;
sum=a[i];
}
}
int beg=1;
while(flag<k-1){
if(ans.count(beg)){
beg++;
continue;
}
else {
ans.insert(beg);
beg++;flag++;
}
}
printf("%d",a[0]);
for(int i=1,j=0;i<m;i++){
if(ans.count(i)){
printf(" /");
ans.erase(i);
}
printf(" %d",a[i]);
}
printf("\n");
}
return 0;
}