题意:这是一道好题,不算很难,但融合了二分和贪心,再加上一点小想法。
二分:这个在刘汝佳的书上有说,不再说了,要求最大值的最小或者最小值的最大,一般都用二分;
贪心:确定了题目的最小的最大值x后,要求前面的序列尽量小,那么贪心策略就是从后面开始时遍历,尽可能在不超过x的情况下,是后面的序列尽可能地大。可以简单反证下,如果序列a加上num后总和不超过x,但选择不加,这个num就会影响后面一些序列的空间,如果后面一些序列空间足够大,可以容得下num,那么就有可能不满足题目里是前面的序列总和尽可能小这个条件。所以这个策略是对的,很容易看出,满足最优子结构。
但我还有一个小问题不懂,要请教各位,题中的最开始L应该是序列中的最大者,但我一开始把L算成是序列中的最小值,结果wa了,但按理说那个正确的值应该不会因l的减小而错啊,小的L区间是包含大的L的区间的,但还是错了,请各位指教。
题目链接:快使用双节棍,哼哼哈嘿
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<functonal>
using namespace std;
typedef long long ll;
int k,m;
ll a[505],sum,minn;
bool vis[505];
bool judge(ll mid)
{
ll ret=0;int cnt=1;
for(int i=0; i<m;i++)
{
if(ret+a[i]>mid)
{ret=a[i];cnt++;}
else ret+=a[i];
}
return cnt<=k;
}
void output(ll r)
{
memset(vis,false,sizeof(vis));
ll ret=0;
int cnt=k;
for(int i=m-1;i>=0;i--)
{
if(ret+a[i]>r||i+1<cnt)//这里没有等于号
{
vis[i]=true;
ret=a[i];
cnt--;
}
else ret+=a[i];
}
for(int i=0;i<m-1;i++)
{
printf("%lld ",a[i]);
if(vis[i]) printf("/ ");
}
printf("%lld\n",a[m-1]);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
minn=-1;
scanf("%d%d",&m,&k);
sum=0;
for(int i=0; i<m; i++)
{
scanf("%lld",&a[i]);
sum+=a[i];
minn=max(a[i],minn);
}
ll l=minn,r=sum,m;//注意这里的l,要取序列中最大值
while(l<r)
{
m=l+(r-l)/2;
if(judge(m))
r=m;
else l=m+1;
}
output(r);
}
return 0;
}