问题:给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
例 1: coins = [1, 2, 5], amount = 12, result =3 (5 + 5 + 2 = 12)
例 2: coins = [2], amount = 3, result = -1
说明:每种硬币的数量是无限的。
我写了两种解法
1、一看到找零问题,马上反应用动态规划,只要知道<amount金额的解,就能马上得出金额为amount的解
2、另一种是递归回溯,应该也属于贪婪法,就是用最大的coin去找零钱,然后如果找不通了,就递归下一面值最大的coin去找零,如果最后还是找不通则回退一步,拿回一张最大coin,然后递归用下一面值最大的coin去找,例如coins=[3,4,5], amount=17, 先用coin=5去找零,找了三张5发现找不通,而且coin=4, coin=3也找不通,拿回一张,用coin=4去找零,找了一张4之后找不通了,但coin=3可以找,最后找完了,result=4(5+5+4+3)
时间复杂度
1、时间空间都是O(nm)
2、不太清楚,应该是不能用主定理算,最差(上限)大概是O(nm)(想象一下最差的情况,对于每个coin都全部回退),比动态规划好,空间是O(1)
#include<iostream>
#include<algorithm>//借用min
#include<vector>
using namespace std;
//动态规划
/*
int main() {
int n;
cin >> n;
vector<int> coins;
coins.push_back(0);
for (int i = 0; i < n; i++) {
int coin;
cin >> coin;
coins.push_back(coin);
}
int amount;
cin >> amount;
vector<vector<int>> table;
for (int i = 0; i <= n; i++) {
table.push_back(vector<int>(amount + 1, -1));
table[i][0] = 0;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= amount; j++) {
if (j < coins[i])
table[i][j] = table[i - 1][j];
else {
if (table[i][j - coins[i]] == -1)
table[i][j] = table[i - 1][j];
else {
if (table[i - 1][j] == -1)
table[i][j] = table[i][j - coins[i]] + 1;
else table[i][j] = min(table[i][j - coins[i]] + 1, table[i - 1][j]);
}
}
}
}
cout << table[n][amount];
}
*/
//递归
int cal(vector<int> coins,int amount) {
if (coins.size() == 0)
return -1;
if (amount == 0)
return 0;
if (amount <coins[coins.size() - 1]) {
coins.pop_back();
return cal(coins, amount);
}
else {
int result = cal(coins, amount - coins[coins.size() - 1]);
if (result != -1)
return result + 1;
else {
coins.pop_back();
return cal(coins, amount);
}
}
}
int main() {
int n;
cin >> n;
vector<int> coins;
for (int i = 0; i < n; i++) {
int coin;
cin >> coin;
coins.push_back(coin);
}
int amount;
cin >> amount;
cout << cal(coins, amount);
}