【LeetCode】零錢兌換(動態規劃、回溯法)

LeetCode第322題鏈接

題目描述:

給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函數來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 -1。

例子:

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

解題:

一、思路:動態規劃;

1.dp[i]表示amount = i時,需要的硬幣數量;填充dp[],因爲dp[]最大爲amount個,所以將其初始化爲amount+1;

2.動態方程:dp[i] = Math.max(dp[i], dp[i - coins[j]] + 1);當前金額i所使用的金幣個數,是dp[i] 和i - coin[j]金額所使用的金幣個數dp[i - coin[i]] + 1,這裏加一是因爲coin[i],消耗一個金幣。

public class Solution {
  public int coinChange(int[] coins, int amount) {
    int max = amount + 1;
     //dp[i] 表示amount = i時,需要的硬幣數量
    int[] dp = new int[amount + 1];//dp[0] 到 dp[amount]
     //填充dp[],因爲dp[]最大爲amount個,所以將其初始化爲amount+1
    Arrays.fill(dp, max);
    dp[0] = 0;
    for (int i = 1; i <= amount; i++) {
      for (int j = 0; j < coins.length; j++) {
        if (coins[j] <= i) {
          dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
        }
      }
    }
    return dp[amount] > amount ? -1 : dp[amount];
  }
}

二、思路:遞歸;

1.先將硬幣面額排序, 用最大面額的金幣去組成amount。剩餘amount1 = amount - n * coins[coins.length - 1];此時需要n個金幣。

2.剩餘金額在用剩餘面額硬幣兌換,再考慮用coins.length - 2個最大面額硬幣, 剩餘金額在用剩餘面額硬幣兌換。【此處需要考慮,如果剩下面額金幣,不能兌換amount1呢?此時需要,將 amount1 變更爲amount1 = amount - (n-1) * coins[coins.length - 1]】再繼續用剩餘的金幣面額去兌換。
3.結束條件:amount == 0時,返回金幣個數。如果amount < 0,即金幣面額不能組成amount,則返回-1;

class Solution {
	int miniAns;
	public int coinChange(int[] coins, int amount) {
		if (amount == 0) return 0;
		Arrays.sort(coins);
		miniAns = Integer.MAX_VALUE;
		coinChangeL(coins, amount, coins.length - 1, 0);
		miniAns = miniAns == Integer.MAX_VALUE? -1: miniAns;
		return miniAns;
	}

	public void coinChangeL(int[] coins, int amount, int index, int cnt) {
		if (amount == 0) {
			miniAns = Math.min(cnt, miniAns);
			return;
		}
		if (index < 0 || amount < 0) {
			return;
		}

		for (int maxcoin = amount/coins[index]; maxcoin >= 0 && maxcoin + cnt < miniAns; maxcoin--) {
			coinChangeL(coins, amount - maxcoin * coins[index], index - 1, cnt + maxcoin);
		}
	}
}

進階-題目描述LeetCode地址

給定不同面額的硬幣和一個總金額。寫出函數來計算可以湊成總金額的硬幣組合數。假設每一種面額的硬幣有無限個。 

例子:

輸入: amount = 5, coins = [1, 2, 5]
輸出: 4
解釋: 有四種方式可以湊成總金額:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

解答:

1.動態規劃

思路:

1.狀態:dp[i]表示金額i的組成次數。很明顯當amount=0時,dp[0] = 1;

2.動態方程:dp[i] = dp[i] + dp[i - coins[j]];

class Solution {
  public int change(int amount, int[] coins) {
    int[] dp = new int[amount + 1];
    dp[0] = 1;

    for (int coin : coins) {
      for (int x = coin; x < amount + 1; ++x) {
        dp[x] += dp[x - coin];
      }
    }
    return dp[amount];
  }
}

2.遞歸法

超時。

class Solution {
    int res;
    public int change(int amount, int[] coins) {
		if (amount == 0 ) return 1;

		Arrays.sort(coins);
        res = 0;
		coinChangeL(coins, amount, coins.length - 1, 0);
		return res;
    }

    public void coinChangeL(int[] coins, int amount, int index, int cnt) {
		if (amount == 0) {
            res++;
			return;
		}
		if (index < 0 || amount < 0) {
			return;
		}

		for (int maxcoin = amount/coins[index]; maxcoin >= 0; maxcoin--) {
			coinChangeL(coins, amount - maxcoin * coins[index], index - 1, cnt + maxcoin);
		}
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章