//给你一个整数数组coins,表示不同面额的硬币;以及一个整数amount, 表示总金额
//计算并返回可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 - 1.
//你可以认为每种硬币的数量是无限的。
#include <iostream>
#include <vector>
using namespace std;
int changeCoins(vector<int>& coins, int amount)
{
// 初始化数组 dp,长度为 amount + 1,因为在 dp 数组中还会存储金额为 0 的情况
// dp[i] 表示想要凑齐 i 元需要的最少硬币个数
// dp[0] 表示想要凑齐 0 元需要的最少硬币个数
// dp[1] 表示想要凑齐 1 元需要的最少硬币个数
// dp[14] 表示想要凑齐 14 元需要的最少硬币个数
// 首先将数组 dp 里面的值都初始化为 -1
// -1 表示当前的金额还没有找到需要的最少硬币个数
vector<int> dp(amount + 1, -1);
// dp[0] 表示想要凑齐 0 元需要的最少硬币个数
dp[0] = 0;
//for循环填充dp[0]到dp[amount]的值,得到dp[amount]最优解
for (int i = 1; i <= amount; i++)
{
//for循环遍历你拥有的零钱的种类,确定哪些零钱能用来拼凑amount的值,并且确定最优的dp[i]的值(最少的硬币数目),即dp[i]的值最小
for (int j = 0; j < coins.size(); j++)
{
//只有硬币的值小于等于当前i的值才能用来拼凑i的值,,,,且如果当前的硬币的值小于i的值的时候,要确保剩余的部分(i - coins[j])是可以拼凑出来的.即
//dp[i - coins[j]]的值不为-1,dp数组的值是从小到大填充的,所以当看i元可不可以拼凑的时候,dp[i - coins[j]]的值必定是有值的,已经处理过的.
if (coins[j] <= i && dp[i - coins[j]] != -1)
{
//到了这里表示i元是可以拼凑出来的,
//如果dp[i]的值为-1则表是这个位置的dp第一次遇到可以拼凑成功的时刻,直接dp[i]= 1(当前遍历的这个硬币) + dp[i - coins[j]](除去当前硬币,剩余的钱需要的硬币数)
//如果dp[i]的值不为-1则表是这个位置的dp不是第一次遇到可以拼凑成功的时刻,比较当前需要的硬币数1(当前遍历的这个硬币) + dp[i - coins[j]](除去当前硬币,剩余的钱需要的硬币数)
//和之前记录的需要的硬币数dp[i],如果1+ dp[i - coins[j]]更小,就用1+ dp[i - coins[j]]把dp[i]更新掉
if (dp[i] == -1 || dp[i] > 1 + dp[i - coins[j]])
{
dp[i] = 1 + dp[i - coins[j]];
}
}
}
}
return dp[amount];
}
int main()
{
vector<int> coins = { 1, 2, 5, 7, 10 };
int amount = 14;
int result = changeCoins(coins, amount);
cout << result << endl;
}