題目大意:
輸入一個m*n的矩陣,按每步走右上、右下、正右(三種選擇)的方法,完成從第一列(任何行)走到最後一列。每步的消費是輸入的矩陣元素值。注意,規定最後一行跟頭一行是相鄰的(即可以相互走到)。
要求輸出 字典序最小(若有多種)的路徑,路徑用每步所在行表示。行列都從1開始計算。輸入 1<=m<=10, 1<=n<=100。
輸出結果不超出int32。
注意:①A path starts anywhere in column 1.
② lexicographically smallest should be output.
③The first and last rows (rows 1 and m) of a matrix are considered adjacent, i.e., the matrix ``wraps'' so that it represents a horizontal cylinder.
題目類型:dp
分析:
①做這題的時候正好碰到UvaOj崩潰(詳見),很不爽。
之前的一直TEL,搞不懂神馬情況(還把別人AC的代碼拿來跟我的測速了,結果還是我的快啊!)。雖然開始的代碼寫得比較挫,但是複雜度神馬的都沒問題啊,難道漏了某個邊界資料造成死迴圈?難道某些資料,我的尋路print_path的方法會耗很多時間?煩,暫時不想去理。
②把dp部分改了改,讓它適合直接記錄下一個結點的位置於next_row數組中(想節省掉列印路徑的時間),然後就是無奈地等UvaOj從崩潰中wake up...我擦,它伺服器至少崩潰了10多個小時吧。交了,AC了。很無奈,以後再說吧,煩死了。
代碼:
#include<cstdio><br />#include<cstring><br />using namespace std;<br />#define MAXM 12<br />#define MAXN 102<br />int a[MAXM][MAXN];<br />int d[MAXM][MAXN], vis[MAXM][MAXN];//下標一律從1開始<br />int n, m;<br />int next_row[MAXM][MAXN];//!!<br />#define INF 1<<30<br />int dp(int i, int j)<br />{<br />if(j > n) return 0;<br />if(vis[i][j]) return d[i][j];<br />vis[i][j] = 1;<br />int dir[3];<br />if(i == 1) dir[0] = 0, dir[1] = 1, dir[2] = -1;<br />else if(i == m) dir[0] = 1, dir[1] = -1, dir[2] = 0;<br />else dir[0] = -1, dir[1] = 0, dir[2] = 1;<br />int min = INF;<br />for(int k = 0; k<3; k++)<br />{<br />int t; int row=(m+i-1+dir[k])%m+1; //統一 使首尾行串連<br />min = min <= (t = dp(row, j+1)+a[i][j])? min : (next_row[i][j] = row, t);//逗號運算式取右值<br />}<br />/*<br />min = min < (t = dp(i, j+1)+a[i][j])? min : t;// i<br />if(i == 1) min = min < (t = dp(m, j+1)+a[i][j])? min : t;//i-1<br />else min = min < (t = dp(i-1, j+1)+a[i][j])? min : t;<br />if(i == m) min = min < (t = dp(1, j+1)+a[i][j])? min : t;//i+1 //!注意這裡的條件結構<br />else min = min < (t = dp(i+1, j+1)+a[i][j])? min : t;<br />*/<br />return d[i][j] = min;<br />}</p><p>void print_path(int ibest)<br />{<br />printf("%d", ibest);//列印序號<br />int row = ibest;<br />for(int j=1; j<n; j++)<br />{<br />row = next_row[row][j];<br />printf(" %d", row);<br />}<br />printf("/n");<br />}</p><p>/*<br />void print_path(int i, int j)<br />{<br />if(j>n) return;<br />if(j == 1) printf("%d", i);//列印序號<br />else printf(" %d", i);<br />if(i == 1)<br />{//按字典序擺放<br />if(d[i][j] == a[i][j] + d[i][j+1]) print_path(i, j+1); //進入某一個,其他不會執行<br />else if(d[i][j] == a[i][j] + d[i+1][j+1]) print_path(i+1, j+1);<br />else if(d[i][j] == a[i][j] + d[m][j+1]) print_path(m, j+1);<br />}<br />if(i == m)<br />{<br />if(d[i][j] == a[i][j] + d[1][j+1]) print_path(1, j+1);<br />else if(d[i][j] == a[i][j] + d[i-1][j+1]) print_path(i-1, j+1);<br />else if(d[i][j] == a[i][j] + d[i][j+1]) print_path(i, j+1);<br />}<br />if(i != 1 && i != m)<br />{<br />if(d[i][j] == a[i][j] + d[i-1][j+1]) print_path(i-1, j+1);//i-1<br />else if(d[i][j] == a[i][j] + d[i][j+1]) print_path(i, j+1);//i<br />else if(d[i][j] == a[i][j] + d[i+1][j+1]) print_path(i+1, j+1); //i+1<br />}<br />}<br />*/<br />int main()<br />{<br />while(scanf("%d%d", &m, &n) != EOF)<br />{<br />memset(vis, 0, sizeof(vis));<br />for(int i=1; i<=m; i++)<br />for(int j=1; j<=n; j++)<br />scanf("%d", &a[i][j]);<br />int ans = INF;<br />int ibest; int t;<br />for(int i=1; i<=m; i++)<br />ans = ans<=(t = dp(i, 1))? ans : (ibest=i, t);//!注意這裡要<= 不然若有多個ibest,則取到的會是字典序最大的ibest了<br />//ans = ans<dp(i, 1)? ans : (ibest=i, dp(i, 1));<br />print_path(ibest);<br />//printf("/n");<br />printf("%d/n", ans);<br />}<br />}