コンピュータのさまざまな分野で1,000冊の電子書籍と100枚の高解像度マインドマップを用意しました。注意を払って[リソース]に返信します。[紹介]に返信してBAT紹介グループに参加することもできます。
前回の記事では、分析を通じて、「三角形の最小経路和」の動的計画問題を無事に完了しました。このセクションでは、この「パスサム」問題を完全に把握するために、同様の質問タイプを引き続き検討します。言うことはあまりありませんが、最初にトピックを見てください:
01、トピック分析
質問64:最小パス合計
非負の整数を含むmxnグリッドがある場合、パス上の数値の合計が最小になるように、左上隅から右下隅へのパスを見つけてください。
注:一度に移動できるのは、1ステップ下または右のみです。
例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
この質問にはある程度の難易度があります!アイデアがない場合は、前の記事の学習内容を確認してください。
ソリューションを直接確認することはお勧めしません。
02、トピックイラスト
まず、トピックを分析し、最小パスを探します。これはどういう意味ですか?m * nの長方形があるとします:[[1,3,1]、[1,5,1]、[4,2,1]]
左上隅から右下隅までの最小パスの合計は、1-3-1-1-1であることが簡単にわかります。このパスでは、結果は7になります。
トピックは明確です、私たちは分析を続けます。この質問は、三角形の最小経路和を見つけるための前の質問と同じです。この質問は、サブ問題の最適解から構築できるということと明確に一致しているため、動的計画法を使用して解決することを検討します。まず、状態を定義します。
dp[i][j] : 表示包含第i行j列元素的最小路径和
同様に、右下隅へのパスはすべて要素[0,0]を通過するためです。したがって、dp [0] [0]を初期化する必要があります。
dp[0][0] = [0][0]位置所在的元素值
分析を続けると、タイトルの条件に従って、dp [i] [j]が必要な場合は、上または左に移動している必要があります。以下に示すように:
5、3または1
2からのみ移動でき、5または4、4からのみ移動、
1
3から移動、1から移動
(赤の位置は青の位置から移動する必要があります)
、状態遷移方程式を取得します。dp [i] [j] = min(dp [i-1] [j]、dp [i] [j-1])+ grid [i] [j]
また、2つの特別なケースを考慮する必要があります。
- 一番上の行は左からのみ移動できます(1-3-1)
- 左端の列は上からのみ移動できます(1-1-4)。
最後に、私たちの目標は左上隅から右下隅まで歩くことであるため、グリッド全体の最小パス合計は実際には最小パス合計です。右下隅に要素が含まれています。これは:
仮定:dpの長さがlで、
最終結果は次のようになります。dp[l-1] [len(dp [l-1])-1]
要約すると、分析が終了し、合計4回実行しました。手順:
1.状態を定義します
。2。状態遷移方程式を要約し
ます。3。状態遷移方程式が満たすことができない特殊なケースを分析します。
4.最終的な解決策を得る
03、GO言語の例
上記の分析によると、コードは次のように取得できます。
![](https://s4.51cto.com/images/blog/202101/27/5486729781abbd79236bd4939d76a6ac.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)func minPathSum(grid [][]int) int {
l := len(grid)
if l < 1 {
return 0
}
dp := make([][]int, l)
for i, arr := range grid {
dp[i] = make([]int, len(arr))
}
dp[0][0] = grid[0][0]
for i := 0; i < l; i++ {
for j := 0; j < len(grid[i]); j++ {
if i == 0 && j != 0 {
dp[i][j] = dp[i][j-1] + grid[i][j]
} else if j == 0 && i != 0 {
dp[i][j] = dp[i-1][j] + grid[i][j]
} else if i !=0 && j != 0 {
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
}
}
}
return dp[l-1][len(dp[l-1])-1]
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
演算結果:
同様に、上記のコードを実行すると、使用されているメモリが大きすぎることがわかります。メモリを圧縮する方法はありますか?観察の結果、左上隅から右下隅までの各ノードの最小パス合計を計算するプロセスでは、以前に蓄積されたデータのみを使用する必要があり、以前の要素データにはアクセスできないことがわかりました。再び。図面は次のとおりです:(このプロセスは掃海のように見えることが誰もがわかります。実際、掃海プラグインを調べると、コアにこの種の分析方法と非常によく似た分析方法があることがわかります。掃海のアルゴリズム。ここでは説明しません。)
最適化されたコードは次のとおりです。
func minPathSum(grid [][]int) int {
l := len(grid)
if l < 1 {
return 0
}
for i := 0; i < l; i++ {
for j := 0; j < len(grid[i]); j++ {
if i == 0 && j != 0 {
grid[i][j] = grid[i][j-1] + grid[i][j]
} else if j == 0 && i != 0 {
grid[i][j] = grid[i-1][j] + grid[i][j]
} else if i !=0 && j != 0 {
grid[i][j] = min(grid[i-1][j], grid[i][j-1]) + grid[i][j]
}
}
}
return grid[l-1][len(grid[l-1])-1]
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
演算結果:
java:
class Solution {
public int minPathSum(int[][] grid) {
int l = grid.length;
if (l < 1) {
return 0;
}
for(int i = 0; i < l; i++) {
for(int j = 0; j < grid[i].length; j++) {
if (i == 0 && j != 0) {
grid[i][j] = grid[i][j-1] + grid[i][j];
} else if (j == 0 && i != 0) {
grid[i][j] = grid[i-1][j] + grid[i][j];
} else if (i !=0) {
grid[i][j] = min(grid[i-1][j], grid[i][j-1]) + grid[i][j];
}
}
}
return grid[l-1][grid[l-1].length - 1];
}
public int min(int a, int b) {
return a > b ? b : a;
}
}
演算結果:
クラス後の考え:パスとクラスの問題と前のサブシーケンスの問題の違いは何ですか?