1. 程式人生 > 實用技巧 >動態規劃之3:二維陣列

動態規劃之3:二維陣列

三角形最小路徑和

三角形最小路徑和

動態規劃基本思路:分析每行的結果,可以得到如下的狀態轉移關係:

\[f[i][j]=\left\{\begin{array}{ll} f[i-1][0]+c[i][0], & j=0 \\ f[i-1][i-1]+c[i][i], & j=i \\ \min (f[i-1][j-1], f[i-1][j])+c[i][j], & \text { otherwise } \end{array}\right. \]

其中,i 表示行,j 表示列。這裡需要注意的是:

  • i 行共有 i+1 個元素
  • 對於每一行的第一個元素和最後元素狀態轉移需要特別處理
  • 邊界條件是:

\[f[0][0]=c[0][0] \]

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
		if(triangle.empty() || triangle[0].empty())
			return 0;
		
        size_t n = triangle.size();
		vector<vector<int>> dp(n,vector<int>(n));          

		dp[0][0] = triangle[0][0];
		for(int i=1;i<n;i++)
		{
            dp[i][0] = dp[i-1][0] + triangle[i][0];
            for(int j=1;j<i;j++)
                dp[i][j] = min(dp[i-1][j-1],dp[i-1][j]) + triangle[i][j];
            dp[i][i] = dp[i-1][i-1] + triangle[i][i];
		}		
		return *std::min_element(dp[n-1].begin(),dp[n-1].end());
	}
};
  • 時間複雜度:O(n^2),n 是矩陣的行數
  • 空間複雜度:O(n^2),n 是矩陣的行數

動態規劃空間優化:從狀態轉移關係中可以看出,第 i 行的結果只與 i-1 有關,因此沒有必要保留那麼多不必要的結果,可以採用兩行(奇偶行)矩陣記錄結果,交替進行。

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int n = triangle.size();
        vector<vector<int>> dp(2, vector<int>(n));
        dp[0][0] = triangle[0][0];
        for (int i = 1; i < n; ++i) {
            int curr = i & 1,prev = 1 - curr;
            dp[curr][0] = dp[prev][0] + triangle[i][0];
            for(int j=1;j<i;j++)
                dp[curr][j] = min(dp[prev][j-1],dp[prev][j]) + triangle[i][j];
            dp[curr][i] = dp[prev][i-1] + triangle[i][i];
        }
        return *min_element(dp[(n - 1) & 1].begin(), dp[(n - 1) & 1].end());
    }
};
  • 時間複雜度:O(n^2),n 是矩陣的行數
  • 空間複雜度:O(n),n 是矩陣的行數

動態規劃自底向上,空間優化:根據上面的基礎,狀態轉移自底向上進行,並且使用一維的 dp 記錄每行的每個節點的最小值:

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int n = triangle.size();
        vector<int> dp(triangle[n-1].begin(),triangle[n-1].end()); //@ 使用最後一行初始化dp陣列

        for(int i = n - 2;i >= 0;--i)
        {
            for(int j=0;j<triangle[i].size();++j)            
                dp[j] = min(dp[j], dp[j+1]) + triangle[i][j];            
        }
        return dp[0];
    }
};
  • 時間複雜度:O(n^2),n 是矩陣的行數
  • 空間複雜度:O(n),n 是矩陣的行數

動態規劃自底向上,原地操作:如果允許原地操作,則:

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int n = triangle.size();   
        for(int i = n- 2;i >= 0;--i)
        {
            for(int j=0;j<triangle[i].size();++j)            
                triangle[i][j] += min(triangle[i+1][j], triangle[i+1][j+1]);            
        }
        return triangle[0][0];
    }
};