cpd算法学习—>动态规划

328 阅读1分钟

动态规划

由于总用 c++刷算法,今天 (2021.9.24) 发现自己对 Java的各种 api运用并不熟练,所以决定从今天起所有的算法都由 Java来写

由此题入门:力扣 322. 零钱兑换

图片.png

方法一:剪枝递归求解

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];
    }
}