动态规划
由于总用 c++刷算法,今天 (2021.9.24) 发现自己对 Java的各种 api运用并不熟练,所以决定从今天起所有的算法都由 Java来写
由此题入门:力扣 322. 零钱兑换
方法一:剪枝递归求解
dp 函数的定义:输入一个目标金额 n,返回能凑出目标金额 n 的最少硬币数量
class Solution {
//优化递归的数组字典
int[] table;
public int coinChange(int[] coins, int amount) {
table= new int[amount+10];
//table数组初始化为特殊值
Arrays.fill(table,-404);
return dp(coins,amount);
}
//dp函数的定义:输入一个目标金额 n,返回能凑出目标金额 n 的最少硬币数量
private int dp(int[] coins, int amount){
if(amount== 0) return 0;
if(amount<0) return -1; //减多了,无法凑出目标值
//查找 table数组,剪枝防止大量的重复递归
if(table[amount]!=-404) return table[amount];
int res= Integer.MAX_VALUE;
//将问题转换为:子问题要凑出金额为 amount- coin时,需要的硬币数为 subDfs+1个
for(int coin: coins){
//计算子问题的结果
int subDfs= dp(coins,amount- coin);
if(subDfs== -1) continue;
//在子问题中选择最优解,然后让需要的硬币+1
res= Math.min(res, subDfs+1);
}
//把计算结果存入字典里
table[amount]= (res == Integer.MAX_VALUE)? -1:res;
return table[amount];
}
}
方法二:dp迭代求解
dp 数组的定义为:当目标金额为 i 时,至少需要 dp[i] 枚硬币凑出
class Solution {
public int coinChange(int[] coins, int amount) {
int dp[]= new int[amount+10];
//dp数组初始化为 amount+1
//因为凑成 amount金额的硬币数最多只能等于 amount(全用 1元硬币)
//初始化为 amount+1则相当于初始化为正无穷,便于后续取最小值
//若初始化为 Integer.MAX_VALUE的话,dp[i-coin]+1会导致溢出
Arrays.fill(dp,amount+1);
dp[0]=0;
for(int i=1;i<=amount;i++){
for(int coin: coins){
//能选才选
if(i-coin>=0) dp[i]= Math.min(dp[i], dp[i-coin]+1);
}
}
return dp[amount]> amount? -1: dp[amount];
}
}