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
#include<iostream>
#include<cstdio>
#include<cstdlib>
int b[],w[],v[],f[];
using namespace std;
int main()
{
int n,num=,t,c;
cin>>n;
for (int i=; i<=n; i++)
cin>>b[i];
for (int i=; i<=n; i++)
{
cin>>c;
t=;
while (t*-<c)//把等值的硬币拆分成几堆
{
num++;
w[num]=t;
v[num]=b[i]*t;
t=t*;
}
num++;
w[num]=c-(t-);
v[num]=b[i]*w[num];
}
int k;
cin>>k;
for (int i=; i<=k; i++)
f[i]=;
f[]=;
for (int i=; i<=num; i++)
for (int j=k; j>=v[i]; j--)
f[j]=min(f[j-v[i]]+w[i],f[j]);
cout<<f[k]<<endl;
return ;
}
Source
http://www.lydsy.com/JudgeOnline/problem.php?id=1531