二分,贪心(Copying Books,uva 714)

来源:互联网 发布:java 双亲委派 编辑:程序博客网 时间:2024/04/30 11:41

最大值尽量小,二分加判断可不可行。这个方法十分重要。

判断方法用贪心。

一些优化:

二分的左边界为最大值,二分的右边界为和。

判断时发现不可行立刻返回。

字典序最小,从右往左贪心即可,因为若右边的多拿了左边的东西,那么左边就有机会拿更左边的东西。

分组不能为空集,所以贪心到后面组不够了,那就直接一人一组。


搞了很久,因为一些算法有问题,所以有死循环没发现。


代码

#include<bits/stdc++.h>#define maxn 550using namespace std;typedef long long ll;int n;int k;int A[maxn];bool bj[maxn];inline bool ok(ll m){    int cnt=0;    ll sum=0;    for(int i=1;i<=n;i++)    {        if(sum+A[i]<=m) sum+=A[i];        else        {            sum=A[i];            cnt++;        }        if(cnt+1>k) return false;    }    cnt++;    return cnt<=k;}inline void print(ll ans){    memset(bj,0,sizeof(bj));    ll sum=0;    int p=n;    int cnt=k;    while(1)    {        if(p==0) break;        if(p==cnt)        {            while(p)            {                bj[p]=true;                p--;            }            break;        }        while(p>=cnt&&sum+A[p]<=ans)        {            sum+=A[p];            p--;        }        bj[p+1]=true;        cnt--;        sum=0;    }    printf("%d",A[1]);    for(int i=2;i<=n;i++)    {        if(bj[i]) printf(" /");        printf(" %d",A[i]);    }    puts("");}int main(){    int N;    scanf("%d",&N);    while(N--)    {        ll sum=0;        int MIN=0;        scanf("%d %d",&n,&k);        for(int i=1;i<=n;i++)        {            scanf("%d",&A[i]);            sum+=A[i];            MIN=max(MIN,A[i]);        }        ll ans;        ll l=MIN;        ll r=sum;        while(l<=r)        {            ll m=(l+r)>>1;            if(ok(m))            {                ans=m;                r=m-1;            }            else l=m+1;        }        print(ans);    }    return 0;}


0 0
原创粉丝点击