この記事では、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つのパスがあります。
- 右->右->下
- 右->下->右
- 下->右->右
例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