POI2005Bank notes银行货币

Description

     Byteotian Bit Bank (BBB) 拥有一套先进的货币系统,这个系统一共有n种面值的硬币,面值分别为b1, b2,..., bn. 但是每种硬币有数量限制,现在我们想要凑出面值k求最少要用多少个硬币.

Input

    第一行一个数 n, 1 <= n <= 200.

    接下来一行 n 个整数b1, b2,..., bn, 1 <= b1 < b2 < ... < b n <= 20 000,

    第三行 n 个整数c1, c2,..., cn, 1 <= ci <= 20 000, 表示每种硬币的个数.

    最后一行一个数k – 表示要凑的面值数量, 1 <= k <= 20 000.

Output

    第一行一个数表示最少需要付的硬币数

Solution

    典型的多重背包,用f[i][j]表示在前i种硬币下,要凑到j元最少需多少个硬币。动态转移方程为:f[i][j]=min(f[i-1][j-v[i]]+1,f[i-1][j])。

    但是,这道题有可能有多个硬币,所以可能会超时,所以用到一种神奇的优化,二进制优化。比如可以用1,2,4,8,5(20-1-2-4-8=5)这5个数进行组合并相加,来得到20以内的任何数。比如说用N个一元,就能把它们合并成面值为1,2,4...等log2N+1个等效硬币,原来N个01的选择变成了log2N+1个,大大降低了时间复杂度。

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 int b[4000],w[4000],v[4000],f[4000100];
 5 using namespace std;
 6 int main()
 7 {
 8     int n,num=0,t,c; 
 9     cin>>n;
10     for (int i=1; i<=n; i++) 
11       cin>>b[i];
12     for (int i=1; i<=n; i++) 
13     {
14       cin>>c;
15       t=1;
16       while (t*2-1<c)//把等值的硬币拆分成几堆
17       {
18         num++;
19         w[num]=t;
20         v[num]=b[i]*t;
21         t=t*2;
22       } 
23       num++; 
24       w[num]=c-(t-1);
25       v[num]=b[i]*w[num];
26     }
27     int k;
28     cin>>k;
29     for (int i=1; i<=k; i++) 
30       f[i]=2100000000;
31     f[0]=0;
32     for (int i=1; i<=num; i++)
33       for (int j=k; j>=v[i]; j--)
34         f[j]=min(f[j-v[i]]+w[i],f[j]); 
35     cout<<f[k]<<endl;
36     return 0;
37 }

Source

    http://www.lydsy.com/JudgeOnline/problem.php?id=1531

posted @ 2016-12-29 17:06  xzy12138  阅读(451)  评论(0编辑  收藏  举报