動態規劃專題——1 矩陣取數

給定一個m行n列的矩陣,矩陣每個元素是一個正整數,你現在在左上角(第一行第一列),你需要走到右下角(第m行,第n列),每次只能朝右或者下走到相鄰的位置,不能走出矩陣。走過的數的總和作爲你的得分,求最大的得分。

初看此題,你的思路是什麼?

(1) 貪心? 先走到大的數再說?看這個例子:
這裏寫圖片描述

無論你以什麼方式走到3,總和都是1 + 1 + 3 + 1 + 1 + 1 + 1 = 9
我們爲了1個3,放棄了那麼多個2, 不值啊。如果我們放棄3而走那些2, 得到的和是1 + 1 + 2 + 2 + 2 + 1 + 1 = 10
看來貪心是錯的! 因爲我們走到最大值時有很多值不能走到了,一個最大值會把我們帶溝裏去。

(2) 枚舉?枚舉是萬能的嘛。

可以試試,有多少條可能的路徑?

你需要往下走(m – 1)次,往右走(n – 1)次,才能走到右下角,一共走m + n – 2步!可行路徑的總條數有C(m + n – 2, m - 1)。
每條路徑長度是m + n – 1 ,所以總的時間複雜度是O(C(m + n – 2, m – 1) * (m + n – 1)), 試試看m = n = 100時,這個數有多大吧!

C(198,99)=22750883079422934966181954039568885395604168260154104734000
這裏寫圖片描述
沒辦法了麼?

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

假設我們從起點走到終點的過程中經過第x行第y列某個位置,爲了從起點到終點得到的和最大,那麼從起點到第x行第 y列這個位置經過的數的和也一定要最大。這幾乎是顯然的,但是你要刨根問底的話,可能要問爲什麼會這樣呢?

我們定義集合A是從起點到第x行第y列的全部路徑集合,定義集合B是從第x行第y列到終點的全部路徑集合。那麼起點到終點的路徑實際上是子路徑a∈A和子路徑b∈B的連接(注意刪掉第x行第y列這個點,否則走了兩次了,呵呵)。

即所有經過第x行第y列的路徑都可以劃分到A和B這兩個集合裏,而且任何a∈A和子路徑b∈B都可以拼接出一條經過第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 – 1,y)或者(x,y – 1)的路徑一定也必須是最優的。

那麼按照我們對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) = -∞, 同理我們也可以定義f(x, 0) = -∞。

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

這裏寫圖片描述

分析一下這個算法的時間複雜度? 顯然是O(m * n),空間複雜度也一樣,因爲我們打出了一張(m + 1) * (n + 1)的表格——因此空間也是O(m * n)的。

這裏寫圖片描述

再走一步?

我們找到了最大的和,如何得到和最大的路徑呢? 還是從遞推式入手,我們發現如果f(x,y) = f(x – 1,y) + A[x][y] 則它是從上面過來的,所以前一個位置是(x – 1, y)
否則 f(x, y) = f(x, y – 1) + A[x][y]則它是從左面過來的,所以前一個位置是(x, y- 1)。

這看起來最優路徑被唯一確定了? 不是的,事實上當f(x – 1,y) = f(x, y – 1)時,前一個位置在上面或者左面都可以——所以路徑還是很多很多的!

通過這個例子,你是不是對動態規劃有了更深刻的理解呢?

最後,由你來寫一段程序,實現這個算法。

輸入

第1行:N,N爲矩陣的大小。(2 <= N <= 500)
第2 - N + 1行:每行N個數,中間用空格隔開,對應格子中獎勵的價值。(1 <= N[i] <= 10000)

輸出

輸出能夠獲得的最大價值。

輸入示例

3
1 3 3
2 1 3
2 2 1

輸出示例

11

自己嘗試着寫寫再看參考代碼哦

http://paste.ubuntu.com/23055442/

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