1. 程式人生 > 其它 >「程式碼隨想錄」本週學習小結!(動態規劃系列五)

「程式碼隨想錄」本週學習小結!(動態規劃系列五)

技術標籤:leecode題解java演算法資料結構動態規劃面試

相信很多小夥伴刷題的時候面對力扣上近兩千道題目,感覺無從下手,我花費半年時間整理了Github專案:leetcode刷題攻略。 裡面有100多道經典演算法題目刷題順序、配有40w字的詳細圖解,常用演算法模板總結,以及難點視訊講解,按照list一道一道刷就可以了!star支援一波吧!

週一

動態規劃:377. 組合總和 Ⅳ中給定一個由正整陣列成且不存在重複數字的陣列,找出和為給定目標正整數的組合的個數(順序不同的序列被視作不同的組合)。

題目面試雖然是組合,但又強調順序不同的序列被視作不同的組合,其實這道題目求的是排列數!

遞迴公式:dp[i] += dp[i - nums[j]];

這個和前上週講的組合問題又不一樣,關鍵就體現在遍歷順序上!

動態規劃:518.零錢兌換II 中就已經講過了。

如果求組合數就是外層for迴圈遍歷物品,內層for遍歷揹包

如果求排列數就是外層for遍歷揹包,內層for迴圈遍歷物品

如果把遍歷nums(物品)放在外迴圈,遍歷target的作為內迴圈的話,舉一個例子:計算dp[4]的時候,結果集只有 {1,3} 這樣的集合,不會有{3,1}這樣的集合,因為nums遍歷放在外層,3只能出現在1後面!

所以本題遍歷順序最終遍歷順序:target(揹包)放在外迴圈,將nums(物品)放在內迴圈,內迴圈從前到後遍歷

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1, 0);
        dp[0] = 1;
        for (int i = 0; i <= target; i++) { // 遍歷揹包
            for (int j = 0; j < nums.size(); j++) { // 遍歷物品
                if (i - nums[j] >= 0 && dp[i] < INT_MAX - dp[i - nums[j]]) {
                    dp[i] += dp[i - nums[j]];
                }
            }
        }
        return dp[target];
    }
};

週二

爬樓梯之前我們已經做過了,就是斐波那契數列,很好解,但動態規劃:70. 爬樓梯進階版(完全揹包)中我們進階了一下。

改為:每次可以爬 1 、 2、…、m 個臺階。問有多少種不同的方法可以爬到樓頂呢?

1階,2階,… m階就是物品,樓頂就是揹包。

每一階可以重複使用,例如跳了1階,還可以繼續跳1階。

問跳到樓頂有幾種方法其實就是問裝滿揹包有幾種方法。

此時大家應該發現這就是一個完全揹包問題了!

和昨天的題目動態規劃:377. 組合總和 Ⅳ基本就是一道題了,遍歷順序也是一樣一樣的!

程式碼如下:

class Solution {
public:
    int climbStairs(int n) {
        vector<int> dp(n + 1, 0);
        dp[0] = 1;
        for (int i = 1; i <= n; i++) { // 遍歷揹包
            for (int j = 1; j <= m; j++) { // 遍歷物品
                if (i - j >= 0) dp[i] += dp[i - j];
            }
        }
        return dp[n];
    }
};

程式碼中m表示最多可以爬m個臺階,程式碼中把m改成2就是本題70.爬樓梯可以AC的程式碼了。

週三

動態規劃:322.零錢兌換給定不同面額的硬幣 coins 和一個總金額 amount。編寫一個函式來計算可以湊成總金額所需的最少的硬幣個數(每種硬幣的數量是無限的)。

這裡我們都知道這是完全揹包。

遞迴公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);

關鍵看遍歷順序。

本題求錢幣最小個數,那麼錢幣有順序和沒有順序都可以,都不影響錢幣的最小個數。

所以本題並不強調集合是組合還是排列。

那麼本題的兩個for迴圈的關係是:外層for迴圈遍歷物品,內層for遍歷揹包或者外層for遍歷揹包,內層for迴圈遍歷物品都是可以的!

外層for迴圈遍歷物品,內層for遍歷揹包:

// 版本一
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 0; i < coins.size(); i++) { // 遍歷物品
            for (int j = coins[i]; j <= amount; j++) { // 遍歷揹包
                if (dp[j - coins[i]] != INT_MAX) { // 如果dp[j - coins[i]]是初始值則跳過
                    dp[j] = min(dp[j - coins[i]] + 1, dp[j]);
                }
            }
        }
        if (dp[amount] == INT_MAX) return -1;
        return dp[amount];
    }
};

外層for遍歷揹包,內層for迴圈遍歷物品:

// 版本二
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {  // 遍歷揹包
            for (int j = 0; j < coins.size(); j++) { // 遍歷物品
                if (i - coins[j] >= 0 && dp[i - coins[j]] != INT_MAX ) {
                    dp[i] = min(dp[i - coins[j]] + 1, dp[i]);
                }
            }
        }
        if (dp[amount] == INT_MAX) return -1;
        return dp[amount];
    }
};

週四

動態規劃:279.完全平方數給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, …)使得它們的和等於 n。你需要讓組成和的完全平方數的個數最少(平方數可以重複使用)。

如果按順序把前面的文章都看了,這道題目就是簡單題了。 dp[i]的定義,遞推公式,初始化,遍歷順序,都是和動態規劃:322. 零錢兌換 一樣一樣的。

要是沒有前面的基礎上來做這道題,那這道題目就有點難度了。

這也體現了刷題順序的重要性

先遍歷揹包,在遍歷物品:

// 版本一
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 0; i <= n; i++) { // 遍歷揹包
            for (int j = 1; j * j <= i; j++) { // 遍歷物品
                dp[i] = min(dp[i - j * j] + 1, dp[i]);
            }
        }
        return dp[n];
    }
};

先遍歷物品,在遍歷揹包:

// 版本二
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i * i <= n; i++) { // 遍歷物品
            for (int j = 1; j <= n; j++) { // 遍歷揹包
                if (j - i * i >= 0) {
                    dp[j] = min(dp[j - i * i] + 1, dp[j]);
                }
            }
        }
        return dp[n];
    }
};

總結

本週的主題其實就是揹包問題中的遍歷順序!

我這裡做一下總結:

求組合數:動態規劃:518.零錢兌換II
求排列數:動態規劃:377. 組合總和 Ⅳ動態規劃:70. 爬樓梯進階版(完全揹包)
求最小數:動態規劃:322. 零錢兌換動態規劃:279.完全平方數

此時我們就已經把完全揹包的遍歷順序研究的透透的了!

就醬,學演算法,認準「程式碼隨想錄」,你會發現相見恨晚!

我是程式設計師Carl,可以找我組隊刷題,也可以在B站上找到我,關注公眾號程式碼隨想錄來和上萬錄友一起打卡學習演算法,來看看,你會發現相見恨晚!

程式碼隨想錄

如果感覺對你有幫助,不要吝嗇給一個