DP問題總結

DP代碼的模版:兩個for 循環,然後在循環裏面寫遞推表達式。返回數組第一個值或最後一個值。要注意的是DP問題的解代碼中是不存在遞歸的。從遍歷各個元素的角度思考遞歸式。

120. Triangle

遞推表達式:

minpath[k][i] = min( minpath[k+1][i], minpath[k+1][i+1]) + triangle[k][i];
int minimumTotal(vector<vector<int> > &triangle) {
    int n = triangle.size();
    vector<int> minlen(triangle.back());
    for (int layer = n-2; layer >= 0; layer--) // For each layer
    {
        for (int i = 0; i <= layer; i++) // Check its every 'node'
        {
            // Find the lesser of its two children, and sum the current value in the triangle with it.
            minlen[i] = min(minlen[i], minlen[i+1]) + triangle[layer][i]; 
        }
    }
    return minlen[0];
}

這題跟這幾題有點像
 

338. Counting Bits

int* countBits(int num, int *returnSize)
{
    num++;
    *returnSize = num;
    int* arr = (int*)malloc(sizeof(int)*num);
    arr[0] = 0;
    for(int i = 1; i < num; i++)
        arr[i] = (i&1)? arr[i>>1]+1 : arr[i>>1];
    return arr;
}

343. Integer Break

列出了4中情況 8 = Max{dp[3] + 5, = dp[3] + 5, 3 + 5, 3 + dp[5]} (i-j = 3, j =5)

class Solution {
    public int integerBreak(int n) {
        int [] dp = new int[n + 1];
        for(int i = 2;i <= n;i++) {
            for(int j = 1;j <= i / 2;j++) {
                dp[i] = Math.max(j * (i - j), dp[i]);
                dp[i] = Math.max(dp[j] * (i - j), dp[i]);
                dp[i] = Math.max(dp[i - j] * j, dp[i]);
                dp[i] = Math.max(dp[i - j] * dp[j], dp[i]);
            }
        }
        return dp[n];
    }
} 

假設j不動,只求dp[i-j] 8 = dp[1]+7,dp[2]+6,dp[3]+5,dp[4]+dp[4],dp[5]+3,dp[6]+2,dp[7]+1

public int integerBreak(int n) {
       int[] dp = new int[n + 1];
       dp[1] = 1;
       for(int i = 2; i <= n; i ++) {
           for(int j = 1; j < i; j ++) {
               dp[i] = Math.max(dp[i], (Math.max(j,dp[j])) * (Math.max(i - j, dp[i - j])));
           }
       }
       return dp[n];
    }

63. Unique Paths II 

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int> > &obstacleGrid) {
        int m = obstacleGrid.size() , n = obstacleGrid[0].size();
        vector<vector<int>> dp(m+1,vector<int>(n+1,0));
        dp[0][1] = 1;
        for(int i = 1 ; i <= m ; ++i)
            for(int j = 1 ; j <= n ; ++j)
                if(!obstacleGrid[i-1][j-1])
                    dp[i][j] = dp[i-1][j]+dp[i][j-1];
        return dp[m][n];
    }
};

221. Maximal Square

 dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;這段代碼的意思是dp[i][j] 臨近的三個位置只要有一個爲0,則爲1,如果都爲1,則加1.

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        if (matrix.empty()) {
            return 0;
        }
        int m = matrix.size(), n = matrix[0].size(), sz = 0;
        vector<vector<int>> dp(m, vector<int>(n, 0));
        for (int j = 0; j < n; j++) {
            dp[0][j] = matrix[0][j] - '0';
            sz = max(sz, dp[0][j]);
        }
        for (int i = 1; i < m; i++) {
            dp[i][0] = matrix[i][0] - '0';
            sz = max(sz, dp[i][0]);
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == '1') {
                    dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
                    sz = max(sz, dp[i][j]);
                }
            }
        }
        return sz * sz;
    }
};

494. Target Sum 

題意是讓在一個數組中的一些數之前添加“+”,其它的數之前添加“-”,從而讓數組之和達到給定的數。

我們將添加“+”的數放入集合P,其它的數放入集合N,於是我們有:
sum(P) - sum(N) = target
sum(P) + sum(N) = sum
於是有sum(P) = (target + sum) / 2,那麼不妨這樣理解題意,從一個數組中選定一些數,使它們的和爲sum(P),如此就變成了很經典的0/1揹包問題,從一個n大小的揹包中選出總和爲sum(P)的方案個數。

狀態表示:dp[i][j]代表前i個數中和爲j的方案個數。
狀態轉移方程:dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]],dp[0][0] = 1
返回結果:dp[n][target],n爲數組大小,target爲sum(P)。

如此時間複雜度爲O(N^2),空間複雜度爲O(M*N)。

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int sum = 0;
        for (auto n : nums) {
        	sum += n;
        }
        /*判斷數組中的元素的和是否能達到目標數*/
        if ((sum + S) % 2 == 1 || sum < S || S < -sum) {
        	return 0;
        }
        int target = (sum + S) / 2;
        vector<int> dp(target + 1, 0);
        /*相同於初始化dp[0][0] = 1*/
        dp[0] = 1;
        for (int i = 0; i < nums.size(); i++) {
        	for (int j = target; j >= nums[i]; j--) {
        		/*利用滾動數組循環更新dp[j],其效果等同於dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]
		         *空間複雜度優化至O(M)。
        		 */
        		dp[j] += dp[j - nums[i]];
        	}
        }
        return dp[target];
    }
};

 139. Word Break

public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        boolean[] f = new boolean[s.length() + 1];     
        f[0] = true;
        for(int i=1; i <= s.length(); i++){
            for(int j=0; j < i; j++){
                if(f[j] && dict.contains(s.substring(j, i))){
                    f[i] = true;
                    break;
                }
            }
        }
        
        return f[s.length()];
    }
}

爲什麼要檢查f[j]是否爲真,若爲真,則從j到i開始匹配,也就是前面的j個字母均匹配成功,看下一個字母是否匹配了。

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