該題是《算法競賽入門經典(第二版)》的一道例題,難度不算大。我先在沒看題解的情況下自己做了一遍,雖然最終通過了,思路與書上的也一樣。但比書上的代碼復雜了很多,可見自己對問題的處理還是有所欠缺。
該題類似於數字三角形問題,處理的方式就是從倒數第二列逐步推到第一列, 每次選擇其後一列權值最小的那條路徑。最終找到花費最小的那個即可。由於出現多個答案時要輸出字典序考前的路徑,所以在選擇路徑的時候如果出現相同,要選擇行數小的那個。我在處理這個問題時用了很多條件語句,使得最終的代碼很長。下面是我的代碼:
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4
5 using namespace std;
6
7 long long a[12][110];
8 int pre[12][110];
9
10 int main()
11 {
12 int m, n, i, j, temw, temr;
13
14 while(scanf("%d%d", &m, &n) == 2)
15 {
16 for(i = 1; i <= m; i++)
17 {
18 for(j = 1; j <= n; j++)
19 {
20 scanf("%lld", &a[i][j]);
21 }
22 }
23 memset(pre, 0, sizeof(pre));
24 for(j = n-1; j >= 1; j--)
25 {
26 for(i = 1; i <= m; i++)
27 {
28 //首先考慮向上方向的路徑
29 if(i == 1) //第一行,向上方向走到最後一行
30 {
31 temw = a[m][j + 1];
32 temr = m;
33 }
34 else
35 {
36 temw = a[i - 1][j + 1];
37 temr = i - 1;
38 }
39 //考慮向正前方向的路徑
40 if(a[i][j + 1] < temw)
41 {
42 temw = a[i][j + 1];
43 temr = i;
44 }
45 else if(a[i][j + 1] == temw)
46 {
47 temr = min(temr, i); //相等取行號小的。
48 }
49 //考慮向下方向的路徑
50 if(i == m) //最後一行,向下走到第一行
51 {
52 if(a[1][j + 1] < temw)
53 {
54 temw = a[1][j + 1];
55 temr = 1;
56 }
57 else if(a[1][j + 1] == temw)
58 {
59 temr = min(temr, 1);
60 }
61 }
62 else
63 {
64 if(a[i + 1][j + 1] < temw)
65 {
66 temw = a[i + 1][j + 1];
67 temr = i + 1;
68 }
69 else if(a[i + 1][j + 1] == temw)
70 {
71 temr = min(temr, i + 1);
72 }
73 }
74 a[i][j] += temw;
75 pre[i][j] = temr; //記錄路徑
76 }
77 }
78 temw = a[1][1];
79 temr = 1;
80 for(i = 2; i <= m; i++) //尋找最小的。
81 {
82 if(a[i][1] < temw)
83 {
84 temw = a[i][1];
85 temr = i;
86 }
87 }
88 //輸出路徑
89 if(n == 1)
90 {
91 printf("%d\n", temr);
92 }
93 else
94 {
95 for(i = temr, j = 1; j <= n-1;)
96 {
97 printf("%d ", i);
98 i = pre[i][j];
99 j++;
100 }
101 printf("%d\n", i);
102 }
103 printf("%lld\n", a[temr][1]);
104 }
105 return 0;
106 }
顯然,在處理三個方向時,用了很多代碼,而書上是這樣做的:
1 int rows[3] = {i, i-1, i+1}; //普通情況下的行號
2 if(i == 0) //書上第一行行號為0,如果是第一行,向上的行號需要改變。
3 rows[1] = m - 1; //最後一列列號為m-1
4 if(i == m-1)
5 rows[2] = 0;
6 d[i][j] = INF; //數組d用來存儲權值
7 sort(rows, rows+3); //先排序,再比較
8 for(int k = 0; k < 3; k++)
9 {
10 int v = d[rows[k]][j+1] + a[i][j];
11 if(v < d[i][j])
12 {
13 d[i][j] = v;
14 next[i][j] = rows[k];
15 }
16 }
顯然,書上通過先排序再比較,這樣從最小的行號開始,找最小權值,找到的最小權值一定是行號最小的,減小了很多代碼量。
還需要說的一點是,這道題我一開始看錯了題意!!!這是比賽的大忌! 根據所給的圖,想當然地以為是從第一行第一列開始走到最後一行最後一列。順便說一下這種情況的我的思路吧。這種情況下,需要一個bool型數組標記每個點是否可到達,首先標記終點可到達,然後從倒數第二列循環,選擇後一列中可以到達且權值最小,行號最小的一條路徑,並標記該點可到達,如果後面沒有可到達的點,就標記該點不可到達。循環到第一列時,只需要考慮起點即可。其他類似於原題。