[USACO13NOV]没有找零No Change
状压Dp
题解:
f[S]表示用了集合S里的coin之后做多购买前多少个。
转移的时候枚举一个coin,二分一下。
统计答案的时候,所有f[S]==n的算出钱数来,取个min。
Code:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
typedef long long ll;
int n,m; ll ans,tot;
ll coin[21],sum[100005];
int f[100005];
int main(){
freopen("a.in","r",stdin);
scanf("%d%d",&m,&n);
for(int i=0;i<m;i++){
scanf("%lld",coin+i);
tot+=coin[i];
}
for(int i=1;i<=n;i++){
scanf("%lld",sum+i);
sum[i]+=sum[i-1];
}
int All=(1<<m)-1;
for(int S=0;S<=All;S++){
for(int i=0;i<m;i++){
if((S>>i)&1){
int x=f[S^(1<<i)];
x=upper_bound(sum+x,sum+n+1,coin[i]+sum[x])-sum-1;
f[S]=max(f[S],x);
}
}
}
ans=1e17;
for(int S=0;S<=All;S++){
// D(S); D(f[S]); E;
if(f[S]==n){
ll res=0;
for(int i=0;i<m;i++){
if((S>>i)&1) res+=coin[i];
}
ans=min(ans,res);
}
}
if(ans>tot) puts("-1");
else printf("%lld\n",tot-ans);
}