leetcode動的二つのプログラミング上の注意

ダイナミックプログラミング

主題分類

一次元DP

DP行列

  • 経路II UNIQUE:マトリックスDP、すべてのメソッドの総数は、シーク
  • パス合計最小:マトリックス型、最大値と最小値を求めて
  • 三角形:マトリックス型、最大値と最小値を求めます
  • スクエア最大:マトリックス型、最大値と最小値を求めて

経路II UNIQUE:マトリックスDP、すべてのメソッドの総数は、シーク

方法:トップダウン
再帰的な方法で、時間制限

class Solution {
    int helper(int[][] g, int x, int y){
        int top = 0, left = 0;
        if(g[x][y] == 1) return 0;
        
        if(x == 0 && y == 0) return 1;
        
        if(x > 0){
            top = helper(g, x - 1, y);
        }
        
        if(y > 0){
            left = helper(g, x, y - 1);
        }
        
        return top + left;
    }
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        return helper(obstacleGrid, obstacleGrid.length - 1, obstacleGrid[0].length -1);
    }
}

方法:トップダウン
再帰的方法+メモ交流

class Solution {
    Map<Long, Integer> m = new HashMap<>();
    
    int helper(int[][] g, int x, int y){
        int top = 0, left = 0;
        if(g[x][y] == 1) return 0;
        
        if(x == 0 && y == 0) return 1;
        
        long key = ((long)x) << 32 | y; // long key = x << 32 | y; 错误
        
        if(m.containsKey(key)) return m.get(key);
        
        if(x > 0){
            top = helper(g, x - 1, y);
        }
        
        if(y > 0){
            left = helper(g, x, y - 1);
        }
        
        m.put(key, top + left);
        return top + left;
    }
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        return helper(obstacleGrid, obstacleGrid.length - 1, obstacleGrid[0].length -1);
    }
}

方法3:ボトムアップから
再帰+メモ
ここで、二次元メモと、実際には、状態遷移は、現在のステップは、前のステップ、その上にのみ一次元メモとの関係を有しています。

class Solution {
    public int uniquePathsWithObstacles(int[][] g) {
        if(g.length == 0) return 0;
        
        int[][] ps = new int[g.length][g[0].length];
        boolean obstacle = false;
        
        for(int i = 0; i < g.length; i ++){
            if(g[i][0] == 1){
                obstacle = true;
            }
            ps[i][0] = obstacle ? 0 : 1;
        }
        
        obstacle = false;
        for(int i = 0; i < g[0].length; i ++){
            if(g[0][i] == 1){
                obstacle = true;
            }
            ps[0][i] = obstacle ? 0 : 1;
        }        
        
        for(int i = 1; i < g.length; i++){
            
            for(int j = 1; j < g[0].length; j++){
                if(g[i][j] == 1){
                    ps[i][j] = 0;
                }        
                else{
                    ps[i][j] = ps[i-1][j] + ps[i][j-1];
                }
            }
        }
        
        return ps[g.length-1][g[0].length-1];
    }
}

パス合計最小:マトリックス型、最大値と最小値を求めて

方法:二次元ボトムアップのメモ+

class Solution {
    public int minPathSum(int[][] g) {
        if (g.length == 0) return 0;
        
        int[][] sum = new int[g.length][g[0].length];
        sum[0][0] = g[0][0];
        
        for(int i = 1; i < g.length; i++){
            sum[i][0] = g[i][0] + sum[i-1][0];
        }
        
        for(int i = 1; i < g[0].length; i++){
            sum[0][i] = g[0][i] + sum[0][i-1];
        }        
        
        for(int i = 1; i < g.length; i ++){
            
            for(int j = 1; j < g[0].length; j++){
                sum[i][j] = g[i][j] + Math.min(sum[i-1][j], sum[i][j-1]);
            }
        }
        
        return sum[g.length-1][g[0].length-1];
    }
}

方法2:1次元ボトムアップのメモ+

class Solution {
    public int minPathSum(int[][] g) {
        if (g.length == 0) return 0;
        
        int[] sum = new int[g[0].length];
        
        sum[0] = g[0][0];
        for(int i = 1; i < g[0].length; i++){
            sum[i] = g[0][i] + sum[i-1];
        }        
        
        for(int i = 1; i < g.length; i ++){
            sum[0] = sum[0] + g[i][0];
            
            for(int j = 1; j < g[0].length; j++){
                sum[j] = g[i][j] + Math.min(sum[j], sum[j-1]);
            }
        }
        
        return sum[g[0].length-1];
    }
}

トライアングル:行列、最小

方法:下部配向から
再帰+メモ
問題が解決され、状態、状態遷移方程式、一方の初期値以下

class Solution {
    Map<Long, Integer> m = new HashMap<>();
    
    int helper(List<List<Integer>> tri, int row, int col){

        long key = (long)row << 32 | col;
        if(m.containsKey(key)){
            return m.get(key);
        }
        
        if ( row == 0 && col == 0 ){  //不能忽略此逻辑
            return tri.get(0).get(0);
        }
        
        int left = Integer.MAX_VALUE;
        int top = Integer.MAX_VALUE;
        if(row > 0){
            if(col < tri.get(row).size() - 1){
                top = helper(tri, row - 1, col);
            }

            if(col > 0){
                left = helper(tri, row - 1, col - 1);
            }
        }
        
        int val = Math.min(top, left) + tri.get(row).get(col);
        m.put(key, val);
        return val;
    }
    
    
    public int minimumTotal(List<List<Integer>> tri) {
        if(tri.size() == 0) return 0;
        
        int sum = Integer.MAX_VALUE;
        for(int j = 0; j < tri.get(tri.size()-1).size(); j++){
            sum = Math.min(sum, helper(tri, tri.size()-1, j));
        }

        return sum;
    }
}

方法2:ボトムアップ
再帰+メモ、以下の方法をさらに最適化することができます

class Solution {
    public int minimumTotal(List<List<Integer>> tri) {
        if(tri.size() == 0) return 0;
        
        int[] memo = new int[tri.get(tri.size()-1).size()];
        memo[0] = tri.get(0).get(0);
        
        for(int i = 1; i < tri.size(); i ++){
            for(int j = tri.get(i).size() -1; j >= 0 ; j--){
                if(j == 0){
                    memo[j] = memo[j] + tri.get(i).get(j);
                }
                else if(j == tri.get(i).size() - 1){
                    memo[j] = memo[j-1] + tri.get(i).get(j);
                }
                else{
                    memo[j] = Math.min(memo[j-1], memo[j]) + tri.get(i).get(j);
                }
            }
        }
        
        int sum = Integer.MAX_VALUE;
        for(int i=0; i<memo.length; i++){
            sum = Math.min(sum, memo[i]);
        }
        
        return sum;
    }
}

スクエア最大:マトリックス型、最大値と最小値を求めて

方法1:ボトムアップからの
再帰+メモ
小さな広場、左上隅の位置くらいの広場を見になることができ、現在の位置を確認するために傾斜して、主要な対角線。ラウンドたくさんの後に現地に湾曲ラウンド。

class Solution {
    public int maximalSquare(char[][] m) {
        if(m.length == 0) return 0;
        
        int[][] memo = new int[m.length + 1][m[0].length + 1];
        
        for(int i = 0; i <= m.length; i++ ){
            memo[i][0] = 0;
        }
        
        for(int i = 0; i <= m[0].length; i++){
            memo[0][i] = 0;
        }
        
        int maxi = 0;
        
        for(int i = 0; i < m.length; i++){
            
            for(int j = 0; j < m[0].length; j++){
                if(m[i][j] != '1'){
                    memo[i+1][j+1] = 0;
                    continue;
                }
                
                if(memo[i][j] <= 0){
                    memo[i+1][j+1] = 1;
                }
                else {
                    int k = 1;
                    for(; k <= memo[i][j]; k++){
                        if(m[i-k][j] != '1' || m[i][j-k] != '1'){
                            break;
                        }
                    }
                    memo[i+1][j+1] = k;
                }
                //System.out.println("i" + i + " j" + j + " =" + memo[i+1][j+1]);
                maxi = Math.max(memo[i+1][j+1], maxi);
            }
        }
        
        return maxi * maxi;
    }
}

おすすめ

転載: www.cnblogs.com/holidays/p/leetcode_note2.html