Leetcodeの典型的な動的計画問題の三部作

この記事では、Leetcodeの3つの古典的な動的計画問題を要約します:最小パス合計、異なるパス1、異なるパス2

1.最小パス合計

タイトル説明

非負の整数を含むmxnグリッドがある場合、パス上の数値の合計が最小になるように、左上隅から右下隅へのパスを見つけてください。

注:一度に移動できるのは、1ステップ下または右のみです。

例:

入力:
[
[1,3,1]、
[1,5,1]、
[4,2,1]
]
出力:7
説明:パス1→3→1→1→ 1インチであるため、最小の合計。

アイデア

典型的な動的計画法、再帰式:

dp[i][j] = grid[i][j]                                     (i=0,j=0)
dp[i][j] = dp[i-1][j]+grid[i][j]                          (i>0,j=0)
dp[i][j] = dp[i][j-1]+grid[i][j]                          (i=0,j>0)
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]       (i>0,j>0)

Javaコード

public class 最小路径和64 {
	public static int minPathSum(int[][] grid) {
	        if(grid == null) return 0;
	        
	        int m = grid.length;
	        int n = grid[0].length;
	        int[][] dp = new int[m][n];
	        
	        for(int i = 0;i<m;i++){
	            for(int j = 0;j<n;j++){
	                if(i == 0 && j == 0){
	                    dp[i][j] = grid[i][j];
	                }else if(i == 0 && j!=0){
	                    dp[i][j] = dp[i][j-1]+grid[i][j];
	                }else if(i!=0 && j==0){
	                    dp[i][j] = dp[i-1][j]+grid[i][j];
	                }
	                else{
	                    dp[i][j] = grid[i][j]+Math.min(dp[i-1][j],dp[i][j-1]);
	                }
	            }
	        }
	        return dp[m-1][n-1];
	    }
	    //测试 main方法入口
	    public static void main(String[] args) {
		int[][] grid = {
   
   {1,3,1},
		  {1,5,1},
		  {4,2,1}};
		int minPathSum2 = minPathSum2(grid);
		System.out.println(minPathSum2);
	}
}

2.別のパス

タイトル説明

ロボットは、mxnグリッドの左上隅にあります(下の図では、開始点は「開始」とマークされています)。

ロボットは一度に1ステップ下または右にしか移動できません。ロボットはグリッドの右下隅に到達しようとします(下の画像で「完了」とラベル付けされています)。
画像

合計でいくつの異なるパスがありますか?

たとえば、上の画像は7 x3グリッドです。可能なパスはいくつありますか?

注:mとnの値は100を超えません。

例1:

入力:m = 3、n = 2
出力:3
説明:
左上隅から始めて、右下隅までの合計3つのパスがあります。

  1. 右->右->下
  2. 右->下->右
  3. 下->右->右
    例2:

入力:m = 7、n = 3
出力:28

アイデア

非常に古典的な動的計画法

  • 終点が境界にある場合、一方通行でなければならないので、
if(i == 0 || j == 0){
	dp[i][j] = 1;
}
  • 終点が境界上にない場合、現在の終点の道路の数は、そのの道路左側のノードの数の合計に等しくなります。つまり、次のようになります。
dp[i][j] = dp[i-1][j]+dp[i][j-1];

Javaコード

public class 不同路径62 {

	public static int uniquePaths(int m, int n) {
	        int[][] dp = new int[m][n];
	        for(int i = 0;i<m;i++){
	            for(int j = 0;j<n;j++){
	                if(i == 0 || j == 0){
	                    dp[i][j] = 1;
	                }else{
	                    dp[i][j] = dp[i-1][j]+dp[i][j-1];
	                }
	            }
	        }
	        return dp[m-1][n-1];
	    }
	
	public static void main(String[] args) {
		int uniquePaths = uniquePaths(3,2);
		System.out.println(uniquePaths);
	}
}

3.異なるパス2

タイトル説明

ロボットは、mxnグリッドの左上隅にあります(下の図では、開始点は「開始」とマークされています)。

ロボットは一度に1ステップ下または右にしか移動できません。ロボットはグリッドの右下隅に到達しようとします(下の画像で「完了」とラベル付けされています)。

画像
ここで、グリッドに障害物があると考えてください。では、左上隅から右下隅まで、いくつの異なるパスがありますか?
グリッド内の障害物と空の位置は、それぞれ1と0で表されます。

注:mとnの値は100を超えません。

例1:

入力:
[
[0,0,0]、
[0,1,0]、
[0,0,0]
]
出力:2
説明:
3x3グリッドの中央に障害物があります。
左上隅から右下隅への2つの異なるパスがあります:
1。右->右->下->下
2.下->下->右->右

アイデア

これは依然として典型的な動的計画法であり、前の質問と比較して障害が追加されています

境界と内部計算を分離して、より明確になるようにします。

  • 境界、境界上の特定のポイントに障害物がある場合、そのポイントまでの道路の数は0です。障害物がない場合、このポイントまでの道路の数は、その前のポイントまでの道路の数と同じです。 。コードで対応する:
//行(左边框)
for(int i = 1;i<rows;i++){
     if(obstacleGrid[i][0] == 1){
         dp[i][0] = 0;
     }else{
         dp[i][0] = dp[i-1][0];
     }
 }
        
//列(上边框)
for(int i = 1;i<columns;i++){
    if(obstacleGrid[0][i] == 1){
        dp[0][i] = 0;
    }else{
        dp[0][i] = dp[0][i-1];
    }
}
  • 内部では、特定のポイント内に障害物がある場合、そのポイントに到達する方法の数は0です。障害物がない場合、そのポイントに到達する方法の数は、に到達する方法の数の合計に等しくなります。その上のノードと左側ノードコードで対応する:
//行列(内部)
for(int i=1;i<rows;i++){
      for(int j = 1;j<columns;j++){
          dp[i][j] = obstacleGrid[i][j] == 1?0:dp[i-1][j]+dp[i][j-1];
      }
  }

Javaコード

public class 不同路径二63 {
	
	public static int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if(obstacleGrid == null||obstacleGrid.length == 0) return 0;
        int rows = obstacleGrid.length;
        int columns = obstacleGrid[0].length;
       
        int[][] dp = new int[rows][columns];
        
        if(obstacleGrid[0][0] == 1){
            dp[0][0] = 0;
        }else{
            dp[0][0] = 1;
        }
        
        //行(左边框)
        for(int i = 1;i<rows;i++){
            if(obstacleGrid[i][0] == 1){
                dp[i][0] = 0;
            }else{
                dp[i][0] = dp[i-1][0];
            }
        }
        
         //列(上边框)
        for(int i = 1;i<columns;i++){
            if(obstacleGrid[0][i] == 1){
                dp[0][i] = 0;
            }else{
                dp[0][i] = dp[0][i-1];
            }
        }
        
        //行列(内部)
        for(int i=1;i<rows;i++){
            for(int j = 1;j<columns;j++){
                dp[i][j] = obstacleGrid[i][j] == 1?0:dp[i-1][j]+dp[i][j-1];
            }
        }
        
        return dp[rows-1][columns-1];
    }
    
	//入口方法 main
	public static void main(String[] args) {
		int[][] obstacleGrid = {
   
   {0,0,0},{0,1,0},{0,0,0}};
		int uniquePathsWithObstacles = uniquePathsWithObstacles(obstacleGrid);
		System.out.println(uniquePathsWithObstacles);
	}
}
输出结果2

おすすめ

転載: blog.csdn.net/fxjzzyo/article/details/88994120