首页 > 代码库 > uva 116 Unidirectional TSP(动态规划,多段图上的最短路)
uva 116 Unidirectional TSP(动态规划,多段图上的最短路)
这道题目并不是很难理解,题目大意就是求从第一列到最后一列的一个字典序最小的最短路,要求不仅输出最短路长度,还要输出字典序最小的路径。
这道题可以利用动态规划求解。状态定义为:
cost[i][j] = max{cost[i+1][j+k]+c[i][j]}(k=-1,0,1)
关于最短路长度的求法,我们可以通过上边的状态转移方程递推求解。cost代表从第i列到第c-1列的最短路,只要找出cost[0][j](j代表行号)中的最大值,我们得到的结果就是最短路。
我们已经得到了最短路的长度。下一步,我们应该如何输出完整的最短路路径。题目说了最短路可能有多个,并且要求输出字典序最小的最短路。
如何得到字典序最小的最短路?
要让字典序最小,那么我们的路径从最左列到最右列,每一列的行号应该尽可能的小。根据我们目前已知的条件,我们可以在第一列中找出属于最短路的最小行号(对应代码行31-34行)。至此我们得到正确路径的开端。然后我们需要去找这条路径上的对应下一列的行号。在这里我们就需要另外一个二维数组nexts(记录当前路径的下一个行号的最小值)。
如何确保是最小值?我们寻找当前状态时是按顺序寻找当前状态的最小值的,因此得到的第一个状态的最小值所对应的行号一定是我们所需要的最小行号。这里特别要注意最后一行的下一行是第一行 这个条件,我起初是按照以下方式去执行三种决策(直行,右上,右下):
1 for(int k = -1;k<=1;k++){2 if(cost[(j+k+r)%r][i+1] <t){3 nexts[j][i] = (j+k+r)%r;4 t = cost[(j+k+r)%r][i+1];5 }6 }
在一般情况下,这处代码不会有问题。可是一旦最短路从跨越了边界,那么行的访问顺序就变得无序了(如r-1,0,1),因此需要一次排序。
完整代码如下:
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 #include <climits> 6 using namespace std; 7 int cost[12][102]; 8 int C[12][102]; 9 int nexts[12][102];10 int main(){11 int r,c;12 while(~scanf("%d%d",&r,&c)){13 for(int i = 0; i < r; i++)14 for(int j = 0; j < c; j++)15 scanf("%d",&C[i][j]);16 for(int i = 0;i<r;i++)cost[i][c] = 0;17 int first= 0,ans = INT_MAX;18 for(int i = c-1;i >= 0;i--){19 for(int j = 0; j < r; j++){20 int t = INT_MAX;21 int ks[] ={-1,0,1};22 ks[0]=(j-1+r)%r,ks[1]=(j+r)%r,ks[2]=(j+1+r)%r;23 sort(ks,ks+3);24 for(int k = 0;k<=2;k++){25 if(cost[ks[k]][i+1] < t){26 nexts[j][i] = ks[k];27 t = cost[ks[k]][i+1];28 }29 }30 cost[j][i] = t + C[j][i] ;31 if(i==0 && cost[j][i] < ans){32 ans = cost[j][i];33 first = j;34 }35 }36 37 }38 int minn = INT_MAX;39 cout << first + 1 ;40 for(int j = nexts[first][0],i=1; i < c;j=nexts[j][i],i++){41 cout <<" "<< j + 1;42 }43 for(int i = 0;i<r;i++){44 minn = min(minn,cost[i][0]);45 }46 cout <<endl<< minn << endl;47 }48 return 0;49 }
uva 116 Unidirectional TSP(动态规划,多段图上的最短路)