51 Nod 1083 矩陣取數問題(動態規劃)

原題鏈接https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1083

題目分析:通過讀題發現我們只能往右邊或者下邊走,意味着“不走回頭路”,就是說矩陣裏面每個位置最多隻會經過一次。其實很多地方是“沒有機會”經過的。比如我現在在第 x 行的第 y 列,不管之前走的路徑是什麼樣子,則它左邊和上邊的位置都是不可能再走到的。也就是說,我先在在矩陣第 x 行的第 y 列,並假設以它爲原點把矩陣分成四個“象限”,只有第四象限的位置纔有可能從這以後經過 (當然還包括橫軸的正半軸)!

假設我們從起點走到終點的過程中經過第 x 行的第 y 列某個位置,爲了從起點到終點得到的和最大,那麼從起點到第 x 行的第 y 列這個位置經過的數的和也一定要最大。我們定義集合 A 是從起點到第 x 行的第 y 列的全部路徑集合,定義集合 B 是從第 x 行的第 y 列到終點的全部路徑集合。那麼起點到終點的路徑實際上是子路徑 aA 和子路徑 bB 的連接(注意刪掉第 x 行的第 y 列這個點,否則走了兩次了)。 即所有經過第 x 行的第 y 列的路徑都可以劃分到 A 和 B 這兩個集合裏,而且任何 aA 和子路徑 bB 都可以拼接出一條經過第 x 行的第 y 列的路徑。那麼我要選擇一條經過 x,y 的能得到最大值的路徑,顯然要選擇 A 集合里路徑和最大的 a,(其實還要選 B 集合裏和路徑和最大的 b)。

說了這麼多,其實就是想明確一個事:從起點到終點的最優路徑上經過了 (m + n - 1) 個點,則這條路徑上對應起點到這 (m + n - 1) 個點的子路徑也都從起點到該位置的所有路徑中和最大的路徑。

那麼假設我們定義 f(int \, x, int \, y) 表示從起點到第 x 行的第 y 列的最優路徑上的數之和,並假設這個矩陣事個二維數組 A[\, ][ \, ] (下標從 1 開始)。我們考慮一下,我們如何才能到(x,y) ?前一步要麼到 (x -1, y), 要麼到 (x, y -1) ,因爲有且只有這兩個位置能到 (x,y),那麼怎樣才能得到 f(x,y) ? 按我們前面說的,如果從起點達到 (x,y) 的最優路徑要經過 (x -1, y)或者 (x, y -1)則,從起點到達 (x, y -1) 或者 (x -1, y) 的路徑一定也必須是最優的。那麼按照我們對 f 的定義,我們有從起點達到 (x,y) 的最優路徑有兩種可能:

  • 要麼 f(x - 1, y) + A[x][y]
  • 要麼 f(x, y - 1) + A[x][y]

我們要取最優,那自然取較大的,因此有 f(x, y) = max(f(x - 1, y) , f(x, y - 1) ) + A[x][y] 。這樣原來要枚舉指數條路徑,現在對於每個位置只有兩種情況啦。有了遞推關係還不夠,有初值才能求解。那我們看一下f(1,1),顯然這是在起點,沒的選f(1,1) = A[1][1]。那麼按照遞推式 f(1,2) = max(f(0, y) , f(1,1)) + A[1][2] , 但是我們對f(0, y) 沒有定義呀!考慮下實際意義,這表示要麼我們從上面到達 (1,2) ,要麼從左面到達 (1,2)。可是上面沒有位置過來啊,這種說明沒的選。所以我們可以定義f(0, y) = -\infty, 同理我們也可以定義f(x, 0) = -\infty

那麼總結一下我們的遞推式:

f(x, y) = \left\{\begin{matrix} -\infty & & (x = 0\, || \,y = 0 )\\ A[1][1]& & (x = 1 \,and\,y = 1)\\ Max(f(x-1,y),f(x, y-1)) + A[x][y]& &(else) \end{matrix}\right.
分析一下這個算法的時間複雜度? 顯然是 O(m * n),空間複雜度也一樣。


代碼如下:

#include <iostream>
#include <algorithm>
using namespace std;

const int INFTY = (1 << 29);

int main() {
	int n, a[505][505], dp[505][505];
	cin >> n;
	
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= n; j++)
			cin >> a[i][j];
	
	for (int i = 0; i <= n; i++) {
		a[0][i] = INFTY;
		a[i][0] = INFTY;
	}
	
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (i == 1 && j == 1 ) dp[i][j] = a[1][1];
			else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + a[i][j];
		}
	}	
	
	cout << dp[n][n] << endl;
	
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章