*leetcode 322. 零钱兑换(动态规划,待研究)

【题目】322. 零钱兑换

  1. 括号生成问题
  2. 组合总和
    给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1

示例 2:

输入: coins = [2], amount = 3
输出: -1

说明:你可以认为每种硬币的数量是无限的。

【解题思路1】贪心+dfs剪枝

先将硬币面额从大到小排序, 再从最大面额的个数开始分支。

  • 先考虑用n个最大面额硬币, 剩余金额在用剩余面额硬币兑换;
  • 再考虑用n-1个最大面额硬币, 剩余金额在用剩余面额硬币兑换…依次类推。
  • 再进行剪枝, 若已用数量已大于最小数量, 则无需再往下讨论。
class Solution {
    int ans=Integer.MAX_VALUE;
    public int coinChange(int[] coins, int amount) {
        Arrays.sort(coins);
        dfs(coins,coins.length-1,amount,0);
        return ans==Integer.MAX_VALUE?-1:ans;
    }
    public void dfs(int[] coins,int index,int amount,int cnt){
        if(index<0){
            return;
        }
        for(int c=amount/coins[index];c>=0;c--){
            int na=amount-c*coins[index];
            int ncnt=cnt+c;
            if(na==0){
                ans=Math.min(ans,ncnt);
                break;//剪枝1
            }
            if(ncnt+1>=ans){
                break; //剪枝2
            }
            dfs(coins,index-1,na,ncnt);
        }
    }
}

【解题思路2】动态规划

例:输入: coins = [1, 2, 5], amount = 11,dp[11] = min (dp[10] + 1, dp[9] + 1, dp[6] + 1)
凑成面值为 11 的最小硬币数可以由以下 33 者的最小值得到:

  • 凑成面值为 10 的最小硬币数 + 面值为 1 的这一枚硬币;
  • 凑成面值为 9 的最小硬币数 + 面值为 2 的这一枚硬币;
  • 凑成面值为 6 的最小硬币数 + 面值为 5 的这一枚硬币;

第 1 步:定义「状态」
dp[i] :凑齐总价值 i 需要的最少硬币数,状态就是问的问题。

第 2 步:写出「状态转移方程」
for i in [0, len - 1] if coin[i] <= amount
dp[amount] = min(1 + dp[amount - coin[i]])

注意的是:

  1. 首先硬币的面值首先要小于等于当前要凑出来的面值;
  2. 剩余的那个面值应该要能够凑出来,例如:求 dp[11] 需要参考 dp[10] ,如果不能凑出来的话,dp[10] 应该等于一个不可能的值,可以设计为 11 + 1,也可以设计为 -1 。
  3. 新状态的值要参考的值以前计算出来的「有效」状态值。这一点在编码的时候需要特别注意。因此,不妨先假设凑不出来,因为比的是小,所以设置一个不可能的数。
class Solution {
    public int coinChange(int[] coins, int amount) {
        // 给 0 占位
        int[] dp = new int[amount + 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 - coin] != amount + 1) {
                    dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
                }
            }
        }

        if (dp[amount] == amount + 1) {
            dp[amount] = -1;
        }
        return dp[amount];
    }
}

【解题思路3】完全背包

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;

        for (int coin : coins) {
            for (int i = coin; i <= amount; i++) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }

        if (dp[amount] == amount + 1) {
            dp[amount] = -1;
        }
        return dp[amount];
    }
}
发布了140 篇原创文章 · 获赞 132 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/XunCiy/article/details/105345071
今日推荐