【算法學習】動態規劃Leetcode習題

動態規劃開始比較繞,所以思考的時候先同自頂向下的方式思考清楚問題的結構,然後再反向自底向上+雙重循環,動態規劃的推導。

343. Integer Break

遞歸子結構核心
遞歸+計劃化搜索方法
自頂向下的方式

// 遞歸+記憶化搜索 
class Solution {
private:
    vector<int> memo;
    int max3(int a, int b, int c){
        return max(a, max(b,c));
    }

    int breakInteger(int n){
        if(n==1)
            return 1;
        if(memo[n] != -1)  
            return memo[n];   //返回記錄

        int res = -1;
        for(int i=1; i<=n-1; ++i)
            res = max3(res, i * (n-i) ,i * breakInteger(n-i));  // 重疊子問題
        memo[n] = res;
        return res;
    }

public:
    int integerBreak(int n) {
        assert(n>=2); //出於嚴謹考慮,讓n>=2,其實根據提議要至少2部分。
        // 調用這個函數的時候,對memo進行初始化
        memo = vector<int>(n+1, -1); //n的問題,初始化爲n+1個元素)
        return breakInteger(n);  //遞歸方法
    }
};

動態規劃方法
自底向上
看出算法複雜度是O(n^2)

// 我自己寫的動態規劃!
class Solution {
private:
    vector<int> memo;
public:
    int integerBreak(int n) {
        assert(n>=2); //出於嚴謹考慮,讓n>=2,其實根據提議要至少2部分。
        // 調用這個函數的時候,對memo進行初始化
        memo = vector<int>(n+1, -1); //n的問題,初始化爲n+1個元素)
        memo[1] = 1;
        memo[2] = 1;
        for(int i=3; i<=n; ++i){
            int res = -1; //用於之後計算memo[i]的最大值
            for(int j=1; j<=i-1; ++j){
                int temp = max(j*memo[i-j],  j*(i-j)); //找出當前這步最大
                if(temp>res)
                    res=temp;
            }
            memo[i] = res;
        }
        return memo[n];

    }
};

老師的優化:就是用再進一步壓縮求max(memo[i])的過程!

// 老師寫的動態規劃!
class Solution {
private:
    int max3(int a, int b, int c){
        return max(a, max(b,c));
    }

public:
    int integerBreak(int n) {
        assert(n>=2); 
        vector<int> memo(n+1,-1);
        memo[1] = 1;
        
        for(int i=2; i<=n; ++i)
            for(int j=1; j<=i-1; ++j)
                // //找出當前這步最大
                memo[i] = max3(memo[i], j*memo[i-j],  j*(i-j) );  
                
       return memo[n];         
    }
    
};

理解:狀態、狀態轉移
狀態——定義函數做什麼(函數代表什麼)
狀態轉移——函數怎麼做

198 House Robber

初始遞歸方式:
函數定義(狀態):考慮搶劫nums[index, … nums.size()]範圍內的所有房子

class Solution {
private:
    // memo[i]表示考慮搶劫nums[i...n]所能獲得的最大收益
    // memo[i]和tryRob狀態的定義是一致的!
    vector<int> memo;

    // 函數定義(狀態):考慮搶劫nums[index, .... nums.size()]範圍內的所有房子
    int tryRob(vector<int> &nums, int index){

        //遞歸終止條件
        if(index >= nums.size())
            return 0;
        if(memo[index] != -1)
            return memo[index];

        int res = 0;
        for(int i=index;i<nums.size(); ++i){  
            res = max(res, nums[i] + tryRob(nums, i+2));
        }
        memo[index] = res;
        return res;
    }
public:
    int rob(vector<int>& nums) {
        memo = vector<int>(nums.size(), -1);
        return tryRob(nums, 0);
    }
};

動態規劃方式:
其中可以這樣拆分:

  1. 這裏用了雙重循環;
    好好斟酌一下用雙重循環和單重循環的區別之處。
  2. memo[i]的獲取方式;
    memo[i]的意思是:第i個位置開始偷到的最大值!所以要有一個max的過程!(取max有技巧的!)
    memo[i] = nums[j] + memo[j+2];

然後目的是求出memo[i]的最大,所以用tip:
memo[i] = max(memo[i], nums[j] + memo[j+2])
然後記得考慮數組越界的情況,詳細見下面代碼!

int rob(vector<int> &nums){
        int n = nums.size();
        if(n==0)
            return 0;

        vector<int> memo(n, -1);
        memo[n-1] = nums[n-1];
        // 遞推解決
        for(int i=n-2; i>=0; --i)
            for(int j=i; j<n; j++)
                // 關鍵這一步,然後max也找出了memo[i]最大值
                memo[i] = max(memo[i], nums[j] + (j+2<n ? memo[j+2]:0));

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