【網格 dp】C003_LC_下降路徑最小和 II(dp)

一、Problem

Given a square grid of integers arr, a falling path with non-zero shifts is a choice of exactly one element from each row of arr, such that no two elements chosen in adjacent rows are in the same column.

Return the minimum sum of a falling path with non-zero shifts.

Input: arr = [[1,2,3],[4,5,6],[7,8,9]]
Output: 13
Explanation: 
The possible falling paths are:
[1,5,9], [1,5,7], [1,6,7], [1,6,8],
[2,4,8], [2,4,9], [2,6,7], [2,6,8],
[3,4,8], [3,4,9], [3,5,7], [3,5,9]
The falling path with the smallest sum is [1,5,7], so the answer is 13.

Constraints:

1 <= arr.length == arr[i].length <= 200
-99 <= arr[i][j] <= 99

二、Solution

方法一:暴力 dp

  • 定義狀態
    • f[i][j]f[i][j]ii 行選第 jj 個數時的最小路徑和
  • 思考初始化:
    • f[0][j]=A[0][j]f[0][j] = A[0][j]
  • 思考狀態轉移方程
    • f[i][j]=A[i][j]+min(f[i1][0...n])f[i][j] = A[i][j] + min(f[i-1][0...n]) 表示位置 (i,j)(i, j) 的值由上一行的最小路徑和 + 當前位置的值組成。
  • 思考輸出min(f[n1][0...n])min(f[n-1][0...n])

這是比較暴力的 dp…

class Solution {
    public int minFallingPathSum(int[][] A) {
    	int n = A.length, INF = 0x3f3f3f3f, f[][] = new int[n][n];
    	for (int j = 0; j < n; j++) f[0][j] = A[0][j];

		for (int i = 1; i < n; i++)
        for (int j = 0; j < n; j++) {
            int lastMin = INF;
            for (int k = 0; k < n; k++) if (k != j && f[i-1][k] < lastMin) {
                lastMin = f[i-1][k];
            }
            f[i][j] = A[i][j] + lastMin;
        }
		int min = INF;
		for (int j = 0; j < n; j++) if (f[n-1][j] < min) {
			min = f[n-1][j];
		}
    	return min;
    }
}

複雜度分析

  • 時間複雜度:O(n3)O(n^3)
  • 空間複雜度:O(n2)O(n^2)

方法二:

上面的代碼用了 O(n)O(n) 的時間去尋找上一行的最小路徑和,其實可以在 for(int j) 同級的地方預處理上一行的最小值到緩存數組中,這就是典型的用空間換取時間。

class Solution {
	int INF = 0x3f3f3f3f, n;
	int[] lastMinArr(int[][] f, int i) {
		int min = INF, sec = INF, mini = 0;
		for (int j = 0; j < n; j++) if (f[i][j] < min) {
            min = f[i][j];
            mini = j;
		}
		for (int j = 0; j < n; j++) if (j != mini && f[i][j] < sec) {
			sec = f[i][j];	
		}
		int[] a = new int[n];
		for (int j = 0; j < n; j++) {
			if (j == mini) a[j] = sec;
			else 		   a[j] = min;
		}
		return a;
	}
    public int minFallingPathSum(int[][] A) {
    	n = A.length; 
    	int f[][] = new int[n][n];
    	for (int j = 0; j < n; j++) f[0][j] = A[0][j];

        int[] a = lastMinArr(f, 0);
		for (int i = 1; i < n; i++) {
			for (int j = 0; j < n; j++) {
				f[i][j] = A[i][j] + a[j];
			}
			a = lastMinArr(f, i);
		}
		int min = INF;
		for (int j = 0; j < n; j++) if (f[n-1][j] < min) {
			min = f[n-1][j];
		}
    	return min;
    }
}

Q:爲什麼在求 lastMinArr 時,需要把 sec 和 min 的位置交換過來?直接數組的所有值都替換爲 min 不好嗎?

A:因爲任意兩個數之間不能是相鄰的,如果相鄰位置就是最小,那麼除去這個最小以外的數,也能保證取到的是最小。

複雜度分析

  • 時間複雜度:O(n2)O(n^2)
  • 空間複雜度:O(n2)O(n^2)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章