[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 }

 

posted @ 2017-10-27 15:17  skylee03  阅读(90)  评论(0编辑  收藏  举报