给你一个整数数组 coins
表示不同面额的硬币,另给一个整数 amount
表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0
。
假设每一种面额的硬币有无限个。
这里我举例就是1分 2分 5分 的硬币三种 ,组成1角(就是10分) ,问有多少种组法
步骤 1:从小金额开始思考
当凑0分 什么都不放 dp[0]=1; 如果不选任何硬币,金额就是 0 分。这是一种“合法”的方式(相当于什么都不做)。因此,dp[0] = 1
表示“用 0 个硬币凑 0 分”有1种方式。
当凑1分
-
只能用 1 个 1分硬币。
-
方法:
[1]
-
所以
dp[1] = 1
。
当凑2分
-
方法 1:
[1,1]
(两个 1分) -
方法 2:
[2]
(一个 2分) -
所以
dp[2] = 2
。
当凑3分
-
方法 1:
[1,1,1]
-
方法 2:
[1,2]
-
所以
dp[3] = 2
。
步骤2 :发现规律
每次用一个新的硬币面额时,可以 “用这个新硬币” 或者 “不用这个新硬币”:
-
如果用新硬币:剩下的金额就是
当前金额 - 新硬币面额
,方法数等于dp[剩余金额]
。 -
如果不用新硬币:方法数保持原来的
dp[当前金额]
。
步骤3:递推公式
dp[当前金额] += dp[当前金额 - 新硬币面额]
代码如下:
int main()
{
vector<int> Coins{ 1,2,5 };
int amount = 10;
vector<int>dp(amount + 1, 0);//初始化为0
dp[0] = 1;
for (int coin : Coins)
{
for (int i = coin; i <= amount; i++)
{
dp[i] = dp[i] + dp[i - coin];
// 等号右边的dp[i]是
它表示 在未考虑当前硬币coin
时,凑出金额i
的已有方法数。
第一次外循环coin为1,只有一种方法,等号右边的dp[i]都为0,第二次外循环,等号右边的dp[i] 为未使用当前coin的方法数 ,此刻的dp[i]为上次循环已经求出的值。
}
}
cout << dp[amount] << endl;
return 0;
}