题目地址:
322.零钱兑换 leetcode地址
518.零钱兑换2 leetcode地址
类似题目:
123.股票问题 leetcode地址
322.零钱兑换
思路:
状态转移方程:dp[i]=min(dp[i-coin])+1 coin是coins的元素。
dp[i]表示凑成总金额i所需的最少的硬币个数。
状态转移方程的含义是:
凑成i所需的最少的硬币个数=凑成总金额(i-coin)所需的最少的硬币个数+1。
比较选出最少的dp[i]。+1表示选择了某个硬币。
比如,coins={1,2,5},那么dp[i]=min(dp[i-1],dp[i-2],dp[i-5])选出最少的硬币个数。
class Solution {
public int coinChange(int[] coins, int amount) {
int dp[]=new int[amount+1];//凑成i最少硬币数量
dp[0]=0;//初始化dp
for(int i=1;i<=amount;i++){
int nums=-1;//如果不能凑成硬币,dp[i]=-1
for(int j=0;j<coins.length;j++){
int tmp=i-coins[j];
if(0<=tmp&&dp[tmp]>=0){
//判断dp[tmp]>=0的原因:i-coins[j]不能凑成硬币的话继续下一次循环
if (nums==-1) {//第一次循环,dp[tmp]里没有值
nums=dp[tmp]+1;
}else{
nums=Math.min(nums,dp[tmp]+1);
}
}
}
dp[i]=nums;
}
return dp[amount];
}
}
518.零钱兑换
题目:给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。
例子:
输入: amount = 5, coins = [1, 2, 5] 输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
思路:这道题可以看做完全背包问题。dp[i][v]表示最多i枚硬币组成总金额v的硬币组合数。
- 接下来找dp[i][v]与之前的状态的关系。之前的状态有哪些?
这是个二维数组,状态类型有两种,一个是硬币的状态,一个是总金额的状态。 - 硬币的状态有几种?对于v=10的情况,coins ={1,2,5},那么可以是10枚硬币组成10元,也可以9枚硬币组成10元,也可以8枚硬币组成10 元等等,dp[i][10]表示最多i枚硬币组成10的组合数,那么dp[10][10]应该包括dp[9][10]的情况,因为最多10枚硬币组成10元的组合数一定包括最多9枚硬币组成10元的组合数。可以知道,状态转移方程大概是dp[i][v]=dp[i-1][v]+?,这样的形式。硬币的状态只与前一个硬币的状态有关。
- 总金额的状态有几种?根据零钱兑换1的思路,可以找一下dp[i][v]与dp[i][v-coin]的关系,即找最多i枚硬币组成总金额v的硬币组合数与最多i枚硬币组成总金额v-coin的硬币组合数的关系,举个例子,v=10,coins ={1,2,5},那么考虑最多10枚硬币组成10元的组合数和最多10枚硬币组成9元、8元、5元的组合数的关系,剩下9元、8元、5元的情况是选择硬币1、2、5之后的情况。
所以,dp[i][v]=dp[i][v-1]+dp[i][v-2]+dp[i][v-5]+...
可以知道,dp[i][v]=sum(dp[i][v-coin]) coin属于coins。
总结可以得到,dp[i][v]=dp[i-1][v]+dp[i][v-coin],coin属于coins。dp[i-1][v]表示第i枚硬币不选,dp[i][v-coin]表示选择第i枚硬币。
class Solution {
public int change(int amount, int[] coins) {
int dp[]=new int[amount+1];
//dp[i]表示组成总金额i的组合数
dp[0]=1;
for(int i=0;i<coins.length;i++){
for(int j=coins[i];j<amount+1;j++){
dp[j]=dp[j]+dp[j-coins[i]];
}
}
return dp[amount];
}
}
代码图解
网友评论