问题描述:
设有n(1<=n<=10)种不同面值的硬币,各硬币的面值存于数组T[1:n]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n]中。
对任意钱数0<=m<=20001,设计一个用最少硬币找钱m的方法。
输入:文件的第一行中只有1 个整数给出n 的值,第2 行起每行2 个数,分别是T[j] 和Coins[j] 。最后1 行是要找的钱数m。
输出:计算出的最少硬币数输出 。问题无解时输出-1。
分析
k重背包+滚动数组
挺像01背包的,相当于把相同币种的硬币当成不同的物品计算
设计dp[i][j]表示在第i个硬币之前,组成金额j的最小硬币数量;
转移方程:dp[i][j] = min( dp[i][j], dp[ i-1 ][ j-v[i] ]+1 );
注意到dp数组的值只由左上层数组的数据更新而来,所以可以采用滚动数组的方式压缩空间占用;
测试样例
输入:
3
1 3
2 3
5 3
18
输出:
5
代码
#include<bits/stdc++.h>
using namespace std;
int n,v[20],num[20],m;
int dp[20010];
int main(){
//freopen("ip.txt","r",stdin);
cin>>n;
for(int i=1;i<=n;i++){
cin>>v[i]>>num[i];
}
cin>>m;
for(int i=1;i<=m;i++) { dp[i]=2000000000; }
for(int i=1;i<=n;i++){
for(int k=1;k<=num[i];k++){
for(int j=m;j>=v[i];j--){ //滚动数组优化
if(j-v[i]>=0) dp[j]=min(dp[j],dp[j-v[i]]+1);
}
}
}
if(dp[m]<2000000000) cout<<dp[m]<<endl;
else cout<<-1<<endl;
return 0;
}