问题描述:
设有n 种不同面值的硬币,各硬币的面值存于数组T[1:n ]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n ]中。对任意钱数0≤m≤20001,设计一个用最少硬币找钱m 的方法。
编程任务:
对于给定的1≤n≤10,硬币面值数组T 和可以使用的各种面值的硬币个数数组Coins, 以及钱数m,0≤m≤20001,编程计算找钱m 的最少硬币数。
数据输入:
由文件input.txt 提供输入数据,文件的第一行中只有1 个整数给出n 的值,第2 行起每行2 个数,分别是T[j] 和Coins[j] 。最后1 行是要找的钱数m。
结果输出:
程序运行结束时,将计算出的最少硬币数输出到文件output.txt 中。问题无解时输出-1。
输入文件示例输出文件示例
input.txt
3
1 3
2 3
5 3
18
output.txt
5
题解
多重背包的模板:
有一个容量为V的背包,有几种不同价值的物品,每种物品都有确定的个数(num>=1),问怎样装包才能使所拿物品价值最大?
和01背包不同,多重背包的每种物品的个数不再是一个,所以拿东西的状态也从拿与不拿变成了拿几个的问题。
因此枚举的策略也有所调整,对于每个容量,我们还要枚举k次来确定拿几个是最优的状况。
递推方程 d p [ k ] = m i n ( d p [ k − T [ i ] ] + 1 , d p [ k ] ) dp[k]=min(dp[k-T[i]]+1,dp[k]) dp[k]=min(dp[k−T[i]]+1,dp[k])的含义为,需要找k元钱时,所花费的最少硬币数。
#include <iostream>
using namespace std;
const int inf = 10086;
int n, m, T[50], coins[50];
int dp[1001];
//多重背包
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> T[i] >> coins[i];
}
cin >> m;
for (int i = 1; i <= m; i++)
dp[i] = inf;
for (int i = 1; i <= n; i++) {
//枚举此面值的数量次
for (int j = 1; j <= coins[i]; j++) {
for (int k = m; k >= T[i]; k--) {
//每一次都是基于上一次的最优值加一
dp[k] = min(dp[k - T[i]] + 1, dp[k]);
}
}
}
if (dp[m] != inf)
cout << dp[m];
else
cout << -1;
return 0;
}