小淵和小軒是好朋友也是同班同窗,他們在一塊兒總有談不完的話題。一次素質拓展活動中,班上同窗安排作成一個 m 行 n 列的矩陣,而小淵和小軒被安排在矩陣對角線的兩端,所以,他們就沒法直接交談了。幸運的是,他們能夠經過傳紙條來進行交流。紙條要經由許多同窗傳到對方手裏,小淵坐在矩陣的左上角,座標 (1,1),小軒坐在矩陣的右下角,座標 (m,n)。從小淵傳到小軒的紙條只能夠向下或者向右傳遞,從小軒傳給小淵的紙條只能夠向上或者向左傳遞。html
在活動進行中,小淵但願給小軒傳遞一張紙條,同時但願小軒給他回覆。班裏每一個同窗均可以幫他們傳遞,但只會幫他們一次,也就是說若是此人在小淵遞給小軒紙條的時候幫忙,那麼在小軒遞給小淵的時候就不會再幫忙。反之亦然。ios
還有一件事情須要注意,全班每一個同窗願意幫忙的好感度有高有低(注意:小淵和小軒的好心程度沒有定義,輸入時用 0 表示),能夠用一個 [0,100] 內的天然數來表示,數越大表示越好心。小淵和小軒但願儘量找好心程度高的同窗來幫忙傳紙條,即找到來回兩條傳遞路徑,使得這兩條路徑上同窗的好心程度之和最大。如今,請你幫助小淵和小軒找到這樣的兩條路徑。數組
app
第一行有兩個用空格隔開的整數 m 和 n,表示班裏有 m 行 n 列。flex
接下來的 m 行是一個 m×n 的矩陣,矩陣中第 i 行 j 列的整數表示坐在第 i 行 j 列的學生的好心程度。每行的 n 個整數之間用空格隔開。優化
spa
輸出文件共一行一個整數,表示來回兩條路上參與傳遞紙條的學生的好心程度之和的最大值。code
1<=m,n<=50orm
以上爲題目內容,已標明出處,若有侵權請聯繫我刪除htm
思路:
能夠看出紙條的傳遞有以下特色:
1.傳過去傳回來是兩條路,可是事實上回來的路程仍然能夠看做另外一段「去程」,沒有區別,因此爲了簡便看做在一個矩陣中找兩條從左上角到右下角的路徑
2.每人只幫一次,也就是兩條路徑不重合,且因爲兩條路的方向只有「右」和「下」,那麼咱們即可以想象出這兩條路徑它們的特色:一條在「左下」一條在「右上」,互不交叉。
因此顯然咱們不能夠搜出好心值最大的一條路以後再搜出第二大的另外一條,由於不交叉的性質會使得第二大的路無合法存在或者具備片面性。
而後根據數據範圍 n, m <= 50,能夠嘗試動態規劃的方式。
開始動態規劃:
因爲以前已提到不能把兩條路分割開來看待,因此咱們須要讓這兩條路「同時推動」。
那麼須要找到表示每一個狀態的方法,考慮到每一個點都是一對座標「x, y」,因此走到某個點的最大好心值能夠用F[x][y]表示
而後有兩條路徑,同時推動時,每一步都會走到兩個點,因此每一步均可以用F[x1][y1][x2][y2]表示當兩條路推動到(x1 , y1)與(x2, y2)點時的最大好心值
顯然,這樣的話,在邊界以內,每一個狀態F[x1][y1][x2][y2]由F[x1 - 1][y1][x2 - 1][y2], F[x1 - 1][y1][x2][y2 + 1], F[x1][y1 - 1][x2 - 1][y2], F[x1][y1 - 1][x2][y2 - 1]四個先前的狀態轉移而來,同時要考慮兩路不交叉的特性,因此當上一個狀態中的(x1 , y1)與(x2, y2)重合時不要加入計算。最後顯然,F[n - 1][m][n][m - 1]就是咱們須要的答案。
複雜度分析
這樣最大所需的存儲空間是int * 50的四次冪 也就是6250000 * 4字節 == 25000000B == 25M,符合範圍要求
時間複雜度是O(m * m * n * n)是6250000接近1e7,此方法是能夠經過此題的,可是若是常數處理不當可能會有問題。並且若是題中的n ,m範圍若擴大到200的話,此方法便行不通了。
核心優化
顯然降維是首要任務,首先咱們能夠發現,四維解法中,因爲兩條路同時推動,有一些狀態是根本不可能存在的,除了(x1 , y1)與(x2, y2)重合的狀態,還有好比F[1][2][3][1],顯然(1 , 3)與(4, 1)分別須要一步與兩步才能走獲得,根本不可能來表示一個狀態。另外,能夠發現,因爲不交叉,因此下面一條路的下方的點都不多是上面的那條路的通過的點。
這樣便發現了大量的不存在的狀態
咱們從條路同時推動時,到達的兩點之間的關係來考慮:
首先上面的那條路的起點必然在(2, 1),下面的起點則在(1, 2),這兩點在同一條左下到右上的斜線上
而過程當中,方向只有「右」和「下」,顯然咱們能夠發現,對一條路來講,它的下一步,老是會走到同一條斜線上(如圖)
那麼顯然,兩條路的下一步確定也會處於同一條左下到右上的斜線上。
而咱們知道,在本題座標系中,這樣的一條斜線上,x + y 等於一個常數,且對於不一樣的在此方向的斜線來講,這個常數是不一樣的
那麼也就是說,
x1 + y1 == x2 + y2
關係找到了。
這樣咱們就能夠開始降維了,由於每一步兩點會處於同一條左下到右上的斜線上,因此能夠用一個維度來表示如今推動到了哪一條斜線上,令C = x + y
同時,咱們知道,與這條斜線垂直方向的斜線上,x - y的值也是一個相似的常數,因此對於目前推動到的這條直線,咱們能夠在上面用x - y的值來表明一條路在此「左下-右上」方向斜線上的位置
因此,另外兩個維度用此點所在的「左上-右下」方向的斜線的」x - y」值表示就能夠了,同時爲了防止出現負數,咱們讓y - x 加 n,令D = n + x - y
同時爲了簡便,在讀入好心值時,咱們就把這些值從x - y座標轉化爲「C - D「座標
因此顯然:
F[C][D1][D2] = max(F[C - 1][D1 + 1][D2 - 1], F[C - 1][D1 - 1][D2 - 1], F[C - 1][D1 + 1][D2 + 1], F[C - 1][D1 + 1][x2 + 1]) + W[C][D1] + W[C][D2]; 同時要注意D1 < D2 恆成立,同時忽略上一步兩點重合的狀況就能夠了
同時,在同一條「左下-右上」斜線上時,咱們循環中的D值應該是遞加、遞減2的
顯然,F[m + n - 1][n + 1][n - 1]即爲咱們要求的結果
最後
相信看到這個數組以後咱們就能夠想到了,C這一維數組空間,徹底是能夠去掉的,由於相鄰的兩層狀態所佔空間不衝突,然後面的狀態只須要上一層的狀態來轉移,正好覆蓋掉上上層狀態完成更替,因此,用F[D1][D2]就能夠了,因此最終結果是F[n + 1][n - 1]
複雜度分析
這樣,每一維最大爲100,所需空間是int * 100 * 100 = 10000B * 4 = 40KB,空間大大減小
時間複雜度最大時小於O(100 / 2 * 100 / 2 * (m + n)) 即小於250000,遠小於限制
AC代碼:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream>
using namespace std; int abss(int a) { return a < 0 ? -a : a; } int n, m; int aa[55][55] = { 0 }; int bb[105][105] = { 0 }; int ff[105][105]; int main() { cin >> m >> n; for (int i = 1; i <= m; ++i) { for (int j = 1; j <= n; ++j) { cin >> aa[i][j]; bb[i + j][m + (j - i)] = aa[i][j];//加m防止出現負數下標 } } ff[m - 1][m + 1] = bb[3][m - 1] + bb[3][m + 1]; for (int i = 4; i < n + m; ++i) { for (int j = abss(i - 2 - m); j < m + n - 1 - abss(n - i + 1); j += 2) { for (int k = m + n - 1 - abss(n - i + 1); k > j; k -= 2) { ff[j][k] = ff[j - 1][k + 1] + bb[i][j] + bb[i][k]; if (j + 1 < k - 1) { ff[j][k] = max(ff[j][k], ff[j + 1][k - 1] + bb[i][j] + bb[i][k]); } if (j + 1 < m + n - 1 - abss(n - i + 2)) { ff[j][k] = max(ff[j][k], ff[j + 1][k + 1] + bb[i][j] + bb[i][k]); } ff[j][k] = max(ff[j][k], ff[j - 1][k - 1] + bb[i][j] + bb[i][k]); } } } printf("%d\n", ff[n - 1][n + 1]); return 0; }
本題從四維時間/四維空間 降爲三維時間/二維空間的關鍵,是找到不存在的無用狀態,並利用方向限制帶來的座標之間的關係,最終將x-y座標進行轉換,從斜線的角度去思考矩陣