1. 程式人生 > >藍橋杯 算法訓練 ALGO-36 傳紙條

藍橋杯 算法訓練 ALGO-36 傳紙條

-s 希望 最小 fine 輸出 class 動態 代碼 res

算法訓練 傳紙條 時間限制:1.0s 內存限制:512.0MB 問題描述   小淵和小軒是好朋友也是同班同學,他們在一起總有談不完的話題。一次素質拓展活動中,班上同學安排做成一個m行n列的矩陣,而小淵和小軒被安排在矩陣對角線的兩端,因此,他們就無法直接交談了。幸運的是,他們可以通過傳紙條來進行交流。紙條要經由許多同學傳到對方手裏,小淵坐在矩陣的左上角,坐標(1,1),小軒坐在矩陣的右下角,坐標(m,n)。從小淵傳到小軒的紙條只可以向下或者向右傳遞,從小軒傳給小淵的紙條只可以向上或者向左傳遞。
  在活動進行中,小淵希望給小軒傳遞一張紙條,同時希望小軒給他回復。班裏每個同學都可以幫他們傳遞,但只會幫他們一次,也就是說如果此人在小淵遞給小軒紙條的時候幫忙,那麽在小軒遞給小淵的時候就不會再幫忙。反之亦然。
  還有一件事情需要註意,全班每個同學願意幫忙的好感度有高有低(註意:小淵和小軒的好心程度沒有定義,輸入時用0表示),可以用一個0-100的自然數來表示,數越大表示越好心。小淵和小軒希望盡可能找好心程度高的同學來幫忙傳紙條,即找到來回兩條傳遞路徑,使得這兩條路徑上同學的好心程度只和最大。現在,請你幫助小淵和小軒找到這樣的兩條路徑。 輸入格式   輸入第一行有2個用空格隔開的整數m和n,表示班裏有m行n列(1<=m,n<=50)。
  接下來的m行是一個m*n的矩陣,矩陣中第i行j列的整數表示坐在第i行j列的學生的好心程度。每行的n個整數之間用空格隔開。 輸出格式   輸出一行,包含一個整數,表示來回兩條路上參與傳遞紙條的學生的好心程度之和的最大值。 樣例輸入 3 3
0 3 9
2 8 5
5 7 0 樣例輸出 34 數據規模和約定   30%的數據滿足:1<=m
,n<=10
  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] 就是四維的 dp[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<cmath>
 3 using namespace std;
 4 
 5 #define MAX_NUM 52
 6 
 7 int map[MAX_NUM][MAX_NUM];     //好心程度 | 權值
 8 int dp[MAX_NUM+MAX_NUM][MAX_NUM][MAX_NUM];
 9 
10 int maxPath(int m, int n)
11 {
12     for (int k = 1;k <= m+n-3; k++)
13     {
14         for (int x1 = 0; x1 <= k; x1++)
15         {
16             for (int x2 = 0; x2 <= k; x2++)
17             {
18                 if (x1 == x2)    //x1 == x2 相當於(x1 == x2 && y1 = y2) 
19                 {
20                     continue;
21                 }
22                 dp[k][x1][x2] = max(max(dp[k-1][x1][x2], dp[k-1][x1-1][x2-1]),
23                                     max(dp[k-1][x1-1][x2], dp[k-1][x1][x2-1]))
24                                 + map[x1][k-x1] + map[x2][k-x2];
25             }
26         }
27     }
28     return dp[m+n-3][m-1][m-2];
29 }
30 
31 int main()
32 {
33     int m, n;
34     scanf("%d%d", &m, &n);
35     
36     for (int i = 0; i < m; i++)
37         for (int j = 0; j < n; j++)
38             scanf("%d", &map[i][j]);
39             
40     int ans = maxPath(m, n);
41     printf("%d\n", ans);
42         
43     return 0;
44 }

示例代碼3 [二維數組]:
 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];
10  
11 int maxPath(int m, int n)  
12 {  
13       
14     for (int k = 1; k <= m+n-3; k++)  
15     {  
16         for (int x1 = m-1; x1 >= 0; x1--)  
17         {  
18             for (int x2 = m-1; x2 > x1; x2--)  
19             {  
20                 if ( k >= x1 && k >= x2)    //x + y = k,當k >= x時,說明還在矩陣範圍之內  
21                 {
22                     dp[x1][x2] = max(max(dp[x1][x2], dp[x1-1][x2-1]), 
23                                      max(dp[x1-1][x2], dp[x1][x2-1])) 
24                                  + map[x1][k-x1] + map[x2][k-x2];
25                 }
26             }  
27         }  
28     }  
29     return dp[m-2][m-1]; 
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 }

藍橋杯 算法訓練 ALGO-36 傳紙條