0%

二维数组最小路径和

题目 (LeetCode #64)

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明: 每次只能向下或者向右移动一步。

示例:

输入:

1
2
3
4
5
[
[1, 3, 1],
[1, 5, 1],
[4, 2, 1]
]

输出: 7

题解

暴力递归

从左上角到右下角, 每次移动只有向右或向下两种可能, 需递归地找出左右两个方向到右下角路径和较小者即可.

暴力递归不记录子状态的结果, F(E) , F(H), F(F) 进行了多次重复计算, 占用较多空间, 数据较多时会超时, 可改用计划搜索的方法进行优化.

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MinimalPathSum {
public int minPathSum(int[][] grid) {
if (grid == null || grid.length < 1 || grid[0] == null || grid[0].length < 1) {
return 0;
}
return process(grid, 0, 0);
}

public static int process(int[][] grid, int i, int j) {
if (i == grid.length - 1 && j == grid[0].length - 1)
return grid[i][j];

if (i == grid.length - 1)
return grid[i][j] + process(grid, i, j + 1);

if (j == grid[0].length - 1)
return grid[i][j] + process(grid, i + 1, j);

return grid[i][j] + Math.min(process(grid, i, j + 1), process(grid, i + 1, j));
}
}
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_minimal_path_sum(grid):
if not grid or len(grid) < 1 or not grid[0] or len(grid[0]) < 1:
return 0
return process(grid, 0, 0)


def process(grid, i, j):
if i == len(grid) - 1 and j == len(grid[0]) - 1:
return grid[i][j]

if i == len(grid) - 1:
return grid[i][j] + process(grid, i, j + 1)
if j == len(grid[0]) - 1:
return grid[i][j] + process(grid, i + 1, j)

return grid[i][j] + min(process(grid, i + 1, j), process(grid, i, j + 1))

计划搜索

将每个子状态的运算结果存入缓存中, 若之前计算过则直接返回计算结果.

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MinimalPathSum {
static HashMap<String, Integer> cache;

public static int minimalPathSum(int[][] grid) {
if (grid == null || grid.length < 1 || grid[0] == null || grid[0].length < 1) {
return 0;
}

cache = new HashMap<>();
return process(grid, 0, 0);
}

public static int process(int[][] grid, int i, int j) {
String key = i + "_" + j;
if (cache.containsKey(key)) {
return cache.get(key); // 存入 Map 时, 已经包含了 grid[i][j] 的值, 故不需重复加
}

int result = 0;
if (i == grid.length - 1 && j == grid[0].length - 1) {
result = grid[i][j];
} else if (i == grid.length - 1) {
result = grid[i][j] + process(grid, i, j + 1);
} else if (j == grid[0].length - 1) {
result = grid[i][j] + process(grid, i + 1, j);
} else {
result = grid[i][j] + Math.min(process(grid, i + 1, j), process(grid, i, j + 1));
}

cache.put(key, result);
return result;
}
}
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class MinimalPathSum:
def __init__(self):
self.result_dict = None

def get_minimal_path_sum(self, grid):
if not grid or len(grid) < 1 or not grid[0] or len(grid[0]) < 1:
return 0

self.result_dict = dict()
return self.process(grid, 0, 0)

def process(self, grid, i, j):
key = str(i) + "_" + str(j)
if self.result_dict.get(key):
return self.result_dict.get(key)

if i == len(grid) - 1 and j == len(grid[0]) - 1:
result = grid[i][j]
elif i == len(grid) - 1:
result = grid[i][j] + self.process(grid, i, j + 1)
elif j == len(grid[0]) - 1:
result = grid[i][j] + self.process(grid, i + 1, j)
else:
result = grid[i][j] + min(self.process(grid, i + 1, j), self.process(grid, i, j + 1))

self.result_dict[key] = result
return result
动态规划

左上角到右下角的最短距离与右下角到左上角最短距离相同, 为便于表示, 以下改为求右下角到左上角的最短距离.

由以上计划搜索部分知, 当矩阵中某点的位置即 ij 的值确定时, 该点到左上角的最短路径就确定了, 故可单独求出矩阵中各元素到左上角的最短路径, 每个元素的最短路径值仅由其相邻的左边和上边元素决定, 其对应关系如下例, 故输出矩阵中的右下角即为左上角到右下角的最短距离.

输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
3	8	6	0	5	9	9	6	3	4	0	5	7	3	9	3	
0 9 2 5 5 4 9 1 4 6 9 5 6 7 3 2
8 2 2 3 3 3 1 6 9 1 1 6 6 2 1 9
1 3 6 9 9 5 0 3 4 9 1 0 9 6 2 7
8 6 2 2 1 3 0 0 7 2 7 5 4 8 4 8
4 1 9 5 8 9 9 2 0 2 5 1 8 7 0 9
6 2 1 7 8 1 8 5 5 7 0 2 5 7 2 1
8 1 7 6 2 8 1 2 2 6 4 0 5 4 1 3
9 2 1 7 6 1 4 3 8 6 5 5 3 9 7 3
0 6 0 2 4 3 7 6 1 3 8 6 9 0 0 8
4 3 7 2 4 3 6 4 0 3 9 5 3 6 9 3
2 1 8 8 4 5 6 5 8 7 3 7 7 5 8 3
0 7 6 6 1 2 0 3 5 0 8 0 8 7 4 3
0 4 3 4 9 0 1 9 7 7 8 6 4 6 9 5
6 5 1 9 9 2 2 7 4 2 7 2 2 3 7 2
7 1 9 6 1 2 7 0 9 6 6 4 4 5 1 0
3 4 9 2 8 3 1 2 6 9 7 0 2 4 2 0
5 1 8 8 4 6 8 5 2 4 1 6 2 2 9 7

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
3	11	17	17	22	31	40	46	49	53	53	58	65	68	77	80	
3 12 14 19 24 28 37 38 42 48 57 62 68 75 78 80
11 13 15 18 21 24 25 31 40 41 42 48 54 56 57 66
12 15 21 27 30 29 25 28 32 41 42 42 51 57 59 66
20 21 23 25 26 29 25 25 32 34 41 46 50 58 62 70
24 22 31 30 34 38 34 27 27 29 34 35 43 50 50 59
30 24 25 32 40 39 42 32 32 36 34 36 41 48 50 51
38 25 32 38 40 47 43 34 34 40 38 36 41 45 46 49
47 27 28 35 41 42 46 37 42 46 43 41 44 53 53 52
47 33 28 30 34 37 44 43 43 46 51 47 53 53 53 60
51 36 35 32 36 39 45 47 43 46 55 52 55 59 62 63
53 37 43 40 40 44 50 52 51 53 56 59 62 64 70 66
53 44 49 46 41 43 43 46 51 51 59 59 67 71 74 69
53 48 51 50 50 43 44 53 58 58 66 65 69 75 83 74
59 53 52 59 59 45 46 53 57 59 66 67 69 72 79 76
66 54 61 65 60 47 53 53 62 65 71 71 73 77 78 76
69 58 67 67 68 50 51 53 59 68 75 71 73 77 79 76
74 59 67 75 72 56 59 58 60 64 65 71 73 75 84 83
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MinimalPathSum {
public static int dynamicProgramming(int[][] grid) {
if (grid == null || grid.length < 1 || grid[0] == null || grid[0].length < 1) {
return 0;
}

int row = grid.length;
int column = grid[0].length;

int[][] dp = new int[row][column];

dp[0][0] = grid[0][0];

for (int i = 1; i < row; i++) {
dp[i][0] = dp[i - 1][0] + grid[i][0];
}

for (int j = 1; j < column; j++) {
dp[0][j] = dp[0][j - 1] + grid[0][j];
}

for (int i = 1; i < row; i++) {
for (int j = 1; j < column; j++) {
dp[i][j] = grid[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]);
}
}

for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
System.out.print(dp[i][j] + "\t");
}
System.out.println();
}

return dp[row - 1][column - 1];
}
}
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MinimalPathSum:
def dynamic_programming(grid):
if not grid or len(grid) < 1 or not grid[0] or len(grid[0]) < 1: return 0

row = len(grid)
column = len(grid[0])

dp = [[0] * column for _ in range(row)]

dp[0][0] = grid[0][0]

for i in range(1, row):
dp[i][0] = grid[i][0] + dp[i - 1][0]

for j in range(1, column):
dp[0][j] = grid[0][j] + dp[0][j - 1]

for i in range(1, row):
for j in range(1, column):
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]

return dp[row - 1][column - 1]