二分查找最小的最大值,然後貪心劃分區間
題解: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;
}