bzoj3312

K个硬币,要买N个物品。

给定买的顺序,即按顺序必须是一路买过去,当选定买的东西物品序列后,付出钱后,货主是不会找零钱的。现希望买完所需要的东西后,留下的钱越多越好,如果不能完成购买任务,输出-1

$$k \leq 16 N \leq 100000$$

考虑状压dp

$dp[STATE]$表示状态为$STATE$的时候能买的最多的东西

记一下最大值 二分一下

。。

#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+5;
typedef long long LL;
inline LL read()
{
    LL x = 0,f = 1;
    char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch=getchar())x = 10*x+ch-'0';
    return x*f;
}
int k,n;
LL dp[maxn],a[maxn],sum[maxn],MAXSTATE,ans;
inline LL find(LL l,LL r,LL x)
{
    LL l1=l,ret=0,top=sum[x]-sum[x-1];
    while(l<=r)
    {
        LL mid=(l+r)/2;
        if (a[mid]-a[l1-1]<=top)ret=mid,l=mid+1;
        else r=mid-1; 
    }
    return ret;
}
int main()
{
    scanf("%d%d",&k,&n);
    for(int i=1;i<=k;i++)sum[i] = sum[i-1] + read();
    for(int i=1;i<=n;i++)a[i]=read()+a[i-1];
    MAXSTATE=(1<<k)-1;ans=2147483233;
    for(int i=0;i<=MAXSTATE-1;i++)
    {
        for(int j=1;j<=k;j++)
            if(!(i&(1<<j-1)))
            {
                LL num=find(dp[i]+1,n,j);
                dp[i|(1<<j-1)]=max(dp[i|(1<<j-1)],num);
                if(num==n)
                {
                    LL tem=0;
                    for(int l=1;l<=k;l++)
                        if((i|(1<<j-1))&(1<<l-1))tem+=sum[l]-sum[l-1];
                    ans=min(ans,tem);
                }
            }
    }
    printf(ans == 2147483233 ? "-1" : "%d" , sum[k]-ans);
}
View Code

 

posted @ 2018-03-20 19:35  探险家Mr.H  阅读(196)  评论(1编辑  收藏  举报