LeetCode [322. 零钱兑换]
题目:
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。
本质上这是一个背包问题,从最后说明中,我们可知,属于完全背包的变种
现在我们对比两个算法,深入理解背包问题
在我上一个博客中,已经介绍过了完全背包问题 https://blog.csdn.net/Librarvl/article/details/97611889
现在我们对比
- 完全背包:给定 物品 i,与其对应的容量 v[i] 和 价值 w[i]。以及背包总容积 V。在不限物品次数,选择最大价值的组合,并返回。
- 硬币选择:给定 币种 i,与其对应的面额 c 。以及需要凑齐总价 amount。在不限每种硬币数量,找到选用硬币数最少的组合,并返回。
在这里我么发现
物品 ==》 币种
容量 ==》 面额
背包体积 ==》 总价 amount
其中每种硬币的价值应该是多少呢?
刚开始我在这里想了很久。其实是思维误区。
首先,完全背包和硬币选择的目标不同,完全背包是选择最大价值的物品,而硬币选择需要找到选择硬币最少的情况,所以我发现,硬币选择中的“价值”应该是硬币数量,对应每个硬币,也就是 1。
价值w[i] ==> 1
由于目标变化,我们的状态表示方程也需要变化
f:硬币数量
i:硬币币种
j:硬币中面额
f[i][j]
:在前 i 个币种,面额为 j 的情况下,所需要最少的 硬币数量
状态转移方程:
f[i][j] = min(f[i-1][j], f[i-1][j - c] + 1);
空间优化后
f[j] = min(f[j], f[j-c] + 1)
代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int Inf = 100000;
int coinChange(vector<int>& coins, int amount) {
vector<int> f(amount + 1, Inf);
f[0] = 0;
for (auto c : coins)
for (int i = c; i <= amount; i++)
f[i] = min(f[i], f[i - c] + 1);
if (f[amount] == Inf) return -1;
return f[amount];
}
};
int main()
{
vector<int> coins = { 1,2,5 };
int amount = 11;
Solution s;
cout << s.coinChange(coins, amount);
cout << "hello" << endl;
system("pause");
return 0;
}