UVA 714 - Copying Books

题意:这是一道好题,不算很难,但融合了二分和贪心,再加上一点小想法。

二分:这个在刘汝佳的书上有说,不再说了,要求最大值的最小或者最小值的最大,一般都用二分;


贪心:确定了题目的最小的最大值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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值