原題鏈接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1083
題目分析:通過讀題發現我們只能往右邊或者下邊走,意味着“不走回頭路”,就是說矩陣裏面每個位置最多隻會經過一次。其實很多地方是“沒有機會”經過的。比如我現在在第 行的第 列,不管之前走的路徑是什麼樣子,則它左邊和上邊的位置都是不可能再走到的。也就是說,我先在在矩陣第 行的第 列,並假設以它爲原點把矩陣分成四個“象限”,只有第四象限的位置纔有可能從這以後經過 (當然還包括橫軸的正半軸)!
假設我們從起點走到終點的過程中經過第 行的第 列某個位置,爲了從起點到終點得到的和最大,那麼從起點到第 行的第 列這個位置經過的數的和也一定要最大。我們定義集合 是從起點到第 行的第 列的全部路徑集合,定義集合 是從第 行的第 列到終點的全部路徑集合。那麼起點到終點的路徑實際上是子路徑 ∈ 和子路徑 ∈ 的連接(注意刪掉第 行的第 列這個點,否則走了兩次了)。 即所有經過第 行的第 列的路徑都可以劃分到 和 這兩個集合裏,而且任何 ∈ 和子路徑 ∈ 都可以拼接出一條經過第 行的第 列的路徑。那麼我要選擇一條經過 的能得到最大值的路徑,顯然要選擇 集合里路徑和最大的 ,(其實還要選 集合裏和路徑和最大的 )。
說了這麼多,其實就是想明確一個事:從起點到終點的最優路徑上經過了 個點,則這條路徑上對應起點到這 個點的子路徑也都從起點到該位置的所有路徑中和最大的路徑。
那麼假設我們定義 表示從起點到第 行的第 列的最優路徑上的數之和,並假設這個矩陣事個二維數組 (下標從 開始)。我們考慮一下,我們如何才能到 ?前一步要麼到 , 要麼到 ,因爲有且只有這兩個位置能到 ,那麼怎樣才能得到 ? 按我們前面說的,如果從起點達到 的最優路徑要經過 或者 則,從起點到達 或者 的路徑一定也必須是最優的。那麼按照我們對 的定義,我們有從起點達到 的最優路徑有兩種可能:
- 要麼
- 要麼
我們要取最優,那自然取較大的,因此有 。這樣原來要枚舉指數條路徑,現在對於每個位置只有兩種情況啦。有了遞推關係還不夠,有初值才能求解。那我們看一下,顯然這是在起點,沒的選。那麼按照遞推式 , 但是我們對 沒有定義呀!考慮下實際意義,這表示要麼我們從上面到達 ,要麼從左面到達 。可是上面沒有位置過來啊,這種說明沒的選。所以我們可以定義, 同理我們也可以定義。
那麼總結一下我們的遞推式:
分析一下這個算法的時間複雜度? 顯然是 ,空間複雜度也一樣。
代碼如下:
#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;
}