算法訓練 傳紙條
時間限制:1.0s 內存限制:512.0MB
問題描述
小淵和小軒是好朋友也是同班同學,他們在一起總有談不完的話題。一次素質拓展活動中,班上同學安排做成一個m行n列的矩陣,而小淵和小軒被安排在矩陣對角線的兩端,因此,他們就無法直接交談了。幸運的是,他們可以通過傳紙條來進行交流。紙條要經由許多同學傳到對方手里,小淵坐在矩陣的左上角,坐標(1,1),小軒坐在矩陣的右下角,坐標(m,n)。從小淵傳到小軒的紙條只可以向下或者向右傳遞,從小軒傳給小淵的紙條只可以向上或者向左傳遞。
在活動進行中,小淵希望給小軒傳遞一張紙條,同時希望小軒給他回復。班里每個同學都可以幫他們傳遞,但只會幫他們一次,也就是說如果此人在小淵遞給小軒紙條的時候幫忙,那么在小軒遞給小淵的時候就不會再幫忙。反之亦然。
還有一件事情需要注意,全班每個同學願意幫忙的好感度有高有低(注意:小淵和小軒的好心程度沒有定義,輸入時用0表示),可以用一個0-100的自然數來表示,數越大表示越好心。小淵和小軒希望盡可能找好心程度高的同學來幫忙傳紙條,即找到來回兩條傳遞路徑,使得這兩條路徑上同學的好心程度只和最大。現在,請你幫助小淵和小軒找到這樣的兩條路徑。
在活動進行中,小淵希望給小軒傳遞一張紙條,同時希望小軒給他回復。班里每個同學都可以幫他們傳遞,但只會幫他們一次,也就是說如果此人在小淵遞給小軒紙條的時候幫忙,那么在小軒遞給小淵的時候就不會再幫忙。反之亦然。
還有一件事情需要注意,全班每個同學願意幫忙的好感度有高有低(注意:小淵和小軒的好心程度沒有定義,輸入時用0表示),可以用一個0-100的自然數來表示,數越大表示越好心。小淵和小軒希望盡可能找好心程度高的同學來幫忙傳紙條,即找到來回兩條傳遞路徑,使得這兩條路徑上同學的好心程度只和最大。現在,請你幫助小淵和小軒找到這樣的兩條路徑。
輸入格式
輸入第一行有2個用空格隔開的整數m和n,表示班里有m行n列(1<=m,n<=50)。
接下來的m行是一個m*n的矩陣,矩陣中第i行j列的整數表示坐在第i行j列的學生的好心程度。每行的n個整數之間用空格隔開。
接下來的m行是一個m*n的矩陣,矩陣中第i行j列的整數表示坐在第i行j列的學生的好心程度。每行的n個整數之間用空格隔開。
輸出格式
輸出一行,包含一個整數,表示來回兩條路上參與傳遞紙條的學生的好心程度之和的最大值。
樣例輸入
3 3
0 3 9
2 8 5
5 7 0
0 3 9
2 8 5
5 7 0
樣例輸出
34
數據規模和約定
30%的數據滿足:1<=
m,
n<=10
100%的數據滿足:1<= m, n<=50
100%的數據滿足:1<= m, n<=50
題目解析:
本道題需用到的算法為動態規划。
題目中提到在 m 行 n 列且帶有權值的矩陣中從(1,1)到(m,n)找一條路徑,然后再從(m,n)到(1,1)找一條路徑,這兩天路徑不能重復,即每個點只能兩個人只能走一次,且不可以回退,即第一條只能向下或向右,第二條只能向上或向左。化簡后可知:其實就是從(1,1)到(m,n)找兩條路徑,這兩條路徑只能向下或向右且不相交,計算出這兩條路徑的權值和的最大值即可。
所以很容易構想出動態規划方程:
兩個人走,利用四維的數組 dp[x1][y1][x2][y2] 來保存路徑中間過程的權值之和的最大值,其中 x1 y1 x2 y2 分別表示兩個人的位置。

每個人現在的位置都有兩種可能:從他的上邊或左邊;兩個人組合就有四種可能,因此:構造出動態規划方程(map[x][y] 表示權值,即好心程度):
dp[x1][y1][x2][y2]=max(dp[x1-1][y1][x2-1][y2],dp[x1][y1-1][x2-1][y2],dp[x1][y1-1][x2][y2-1],dp[x1-1][y1][x2][y2-1])+map[x1][y1]+map[x2][y2];
其中 x1,x2 的取值范圍為從起點到終點,即 1 ~ m,y1,y2 的取值范圍為起點到終點,即 1 ~ n。
此方程的時間復雜度為 O(n4)。因此可以進一步優化:
假如現在是 5 x 5 的矩陣,每個人從起點走三步,會出現四種情況。

這四種情況的坐標分別為:(0,3)(1,2) (2,1) (3,0)。通過這四個坐標,發現一個規律: 0 + 3 = 1 + 2 = 2 + 1 = 3 + 0 = 3 = k (k為走的步數)。所有,x1 + x2 = k , x2 + y2 = k。所以,y = k - x。因此,三維的動態規划方程為:
dp[k][x1][x2]=max(dp[k-1][x1][x2],dp[k-1][x1-1][x2-1],dp[k-1][x1-1][x2],dp[k-1][x1][x2-1])+map[x1][k-x1]+map[x2][k-x2];
其中,dp[k][x1][x2] 就是四維的 dp[x1][y1][x2][y2],dp[k-1][x1][x2] 就是四維的 dp[x1][y1-1][x2][y2-1],map[x1][k-x1] 就是四維的 map[x1][y1],以此類推。

終點的坐標為 (m-1,n-1),但是 k 不能到達終點這個位置,因為違背了題目中兩個人不能重復,k 的最大情況為(m-1)+(n-1)- 1,k 在最小情況也就是 2 x 2 的矩陣中取得最小值 1,所以 k 的取值范圍為 1 ~
m+n-3。x1 和 x2
的取值范圍都為從起點(0,0)到最大步數 k,即 0 ~ k。
此方程的時間復雜度為 O(n3)。因此還可以進一步優化:
從三維的動態規划方程可以發現,前一步總是 k - 1,所以,二維的動態規划方程可以優化為:
dp[x1][x2] = max(dp[x1][x2], dp[x1 - 1][x2 - 1], dp[x1 - 1][x2], dp[x1][x2 - 1]) + map[x1][k - x1] + map[x2][k - x2];

根據三維時的分析,兩條路徑都走不到終點,所以讓第一個人走到終點的上方,第二個人走到終點的左方,k 的取值范圍為 1 ~ (m-1)+(n-1)- 1,最終要輸出的結果為 dp[m-2][m-1]。
此方程的時間復雜度為
O(n2)。
示例代碼1 [四維數組]:
1 #include <iostream> 2 #include<stdio.h> 3 #include<cmath> 4 using namespace std; 5 6 #define MAX_NUM 52 7 8 int map[MAX_NUM][MAX_NUM]; //好心程度 | 權值 9 int dp[MAX_NUM][MAX_NUM][MAX_NUM][MAX_NUM]; 10 11 int maxPath(int m, int n) 12 { 13 for (int x1 = 1; x1 <= m; x1++) 14 { 15 for (int y1 = 1; y1 <= n; y1++) 16 { 17 for (int x2 = 1; x2 <= m; x2++) 18 { 19 for (int y2 = 1; y2 <= n; y2++) 20 { 21 /* 22 如果第一個人沒有走到最后一行或最后一列,並且兩個人沒有重復 23 因為走到最后一行或最后一列,容易造成第二個人無路可走的情況 24 */ 25 if ((x1 < m || y1 < n) && x1 == x2 && y1 == y2) 26 { 27 continue; 28 } 29 dp[x1][y1][x2][y2] = max( max(dp[x1-1][y1][x2-1][y2], dp[x1-1][y1][x2][y2-1]), 30 max(dp[x1][y1-1][x2-1][y2], dp[x1][y1-1][x2][y2-1])) 31 + map[x1][y1] + map[x2][y2]; 32 } 33 } 34 } 35 } 36 return dp[m][n][m][n]; 37 } 38 39 int main() 40 { 41 int m, n; 42 scanf("%d%d", &m, &n); 43 44 for (int i = 1;i <= m; i++) 45 for (int j = 1;j <= n; j++) 46 scanf("%d", &map[i][j]); 47 48 int ans = maxPath(m, n); 49 printf("%d\n", ans); 50 51 return 0; 52 }
示例代碼2 [三維數組]:
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 using namespace std; 5 6 #define MAX_NUM 52 7 8 int map[MAX_NUM][MAX_NUM]; //好心程度 | 權值 9 int dp[MAX_NUM+MAX_NUM][MAX_NUM][MAX_NUM]; 10 11 int maxPath(int m, int n) 12 { 13 for (int k = 1;k <= m+n-3; k++) 14 { 15 for (int x1 = 0; x1 <= k; x1++) 16 { 17 for (int x2 = 0; x2 <= k; x2++) 18 { 19 if (x1 == x2) //x1 == x2 相當於(x1 == x2 && y1 = y2) 20 { 21 continue; 22 } 23 dp[k][x1][x2] = max(max(dp[k-1][x1][x2], dp[k-1][x1-1][x2-1]), 24 max(dp[k-1][x1-1][x2], dp[k-1][x1][x2-1])) 25 + map[x1][k-x1] + map[x2][k-x2]; 26 } 27 } 28 } 29 return dp[m+n-3][m-1][m-2]; 30 } 31 32 int main() 33 { 34 int m, n; 35 scanf("%d%d", &m, &n); 36 37 for (int i = 0; i < m; i++) 38 for (int j = 0; j < n; j++) 39 scanf("%d", &map[i][j]); 40 41 int ans = maxPath(m, n); 42 printf("%d\n", ans); 43 44 return 0; 45 }
示例代碼3 [二維數組]:
1 #include<iostream> 2 #include<cstdio> 3 #include<string.h> 4 #include<cmath> 5 using namespace std; 6 7 #define MAX_NUM 52 8 9 int map[MAX_NUM][MAX_NUM]; //好心程度 | 權值 10 int dp[MAX_NUM][MAX_NUM]; 11 12 int maxPath(int m, int n) 13 { 14 memset(dp, 0, sizeof(dp)); 15 for (int k = 1; k <= m+n-3; k++) 16 { 17 for (int x1 = m-1; x1 >= 0; x1--) 18 { 19 for (int x2 = m-1; x2 > x1; x2--) 20 { 21 if ( k >= x1 && k >= x2) //x + y = k,當k >= x時,說明還在矩陣范圍之內 22 { 23 dp[x1][x2] = max(max(dp[x1][x2], dp[x1-1][x2-1]), 24 max(dp[x1-1][x2], dp[x1][x2-1])) 25 + map[x1][k-x1] + map[x2][k-x2]; 26 } 27 } 28 } 29 } 30 return dp[m-2][m-1]; 31 } 32 33 int main() 34 { 35 int m, n; 36 scanf("%d %d", &m, &n); 37 38 for (int i = 0;i < m; i++) 39 for (int j = 0; j < n; j++) 40 scanf("%d", &map[i][j]); 41 42 int ans = maxPath(m, n); 43 printf("%d\n", ans); 44 45 return 0; 46 }