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