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); }