[USACO13NOV]No Change
题目大意:
你有k(k<=16)个硬币,每个硬币都有自己的面值。
现在你要给n件商品付钱,每件商品也有自己的价格。
然而老板是个奸商,他绝对不会给你找钱。
你每次付钱只能用一个硬币,但是你可以一次性买很多商品。
问你最后最多还能留下多少钱。
思路:
状压DP。
f[i]表示状态为i时能买的商品数,i表示你用了哪些硬币。
从小到大枚举每个状态i,然后枚举状态i中的硬币j,是这次付款用的硬币。
二分找一下这些硬币最多能买前面连续的多少个商品。
如果j不在i中,就算作最后剩下的硬币。
1 #include<cstdio> 2 #include<cctype> 3 #include<algorithm> 4 inline int getint() { 5 register char ch; 6 while(!isdigit(ch=getchar())); 7 register int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 const int K=16,N=100001; 12 int v[K],sum[N],f[1<<K]; 13 int main() { 14 const int k=getint(),n=getint(); 15 for(register int i=0;i<k;i++) { 16 v[i]=getint(); 17 } 18 for(register int i=1;i<=n;i++) { 19 sum[i]=sum[i-1]+getint(); 20 } 21 int ans=-1; 22 for(register int i=0;i<(1<<k);i++) { 23 int rem=0; 24 for(register int j=0;j<k;j++) { 25 if(i&(1<<j)) { 26 f[i]=std::max(f[i],int(std::upper_bound(&sum[0],&sum[n+1],sum[f[i^(1<<j)]]+v[j])-sum-1)); 27 } else { 28 rem+=v[j]; 29 } 30 } 31 if(f[i]==n) { 32 ans=std::max(ans,rem); 33 } 34 } 35 printf("%d\n",ans); 36 return 0; 37 }