例题 9-4 单向TSP(Unidirectional TSP,UVa116)
VJ传送门:https://cn.vjudge.net/problem/UVA-116
描述:
给定一个m行n列的矩阵(m <= 10, n <= 100),要求从第1列的任何一行出发,每次沿右或右下或右上到达后面一列,最后到第m列任何一行,使经过的整数之和最小,整个矩阵是环形的,即第一行的上一行是最后一行,最后一行的下一行是第一行。多解时输出字典序最小的。
输入:
5 6 3 4 1 2 8 6 6 1 8 2 7 4 5 9 3 9 9 5 8 4 1 3 2 6 3 7 2 8 6 4 5 6 3 4 1 2 8 6 6 1 8 2 7 4 5 9 3 9 9 5 8 4 1 3 2 6 3 7 2 1 2 3 2 2 9 10 9 10
输出:
1 2 3 4 4 5 16 1 2 1 5 4 5 11 1 1 19
分析:
在这个题目中,每一列就是一个阶段,每个阶段都有3种决策:直行、右上和右下。
状态定义:d(i,j)为从格子(i,j)出发到最后一列的最小开销。
题目要求输出字典序最小的解,则需要在计算d(i,j)的同时记录下下一列最小的行号(在满足最优性的前提下),同时使用逆推。
代码:
1 //例题9-4 单向TSP(Unidirectional TSP, UVa116) 2 //VJ传送门:https://cn.vjudge.net/problem/UVA-116 3 #include <bits/stdc++.h> 4 5 using namespace std; 6 const int maxn = 100 + 5; 7 const int INF = 1000000000; 8 int m, n, a[maxn][maxn], d[maxn][maxn], nexts[maxn][maxn]; 9 10 int main() 11 { 12 while (scanf("%d %d", &m, &n) == 2 && m) 13 { 14 for (int i = 0; i < m; i++) 15 for (int j = 0; j < n; j++) 16 scanf("%d", &a[i][j]); 17 18 int ans = INF, first = 0; 19 for (int j = n - 1; j >= 0; j--) //从最后一列逆推 20 { 21 for (int i = 0; i < m; i++) 22 { 23 if (j == n - 1) d[i][j] = a[i][j]; //边界条件 24 else 25 { 26 int rows[3] = { i, i - 1, i + 1 }; //三个方向 27 if (i == 0) rows[1] = m - 1; //第一行上面是最后一行 28 if (i == m - 1) rows[2] = 0; //最后一行后面是第一行 29 sort(rows, rows + 3); //重新排序,以找到字典序最小的 30 d[i][j] = INF; 31 for (int k = 0; k < 3; k++) //三种决策 32 { 33 int tmp = d[rows[k]][j+1] + a[i][j]; 34 if (tmp < d[i][j]) { d[i][j] = tmp; nexts[i][j] = rows[k]; } 35 } 36 } 37 //确定第一列的起始在第几行 38 if (j == 0 && d[i][j] < ans) { ans = d[i][j]; first = i; } 39 } 40 } 41 printf("%d", first + 1); 42 for (int i = nexts[first][0], j = 1; j < n; i = nexts[i][j], j++) 43 printf(" %d", i + 1); 44 printf("\n%d\n", ans); 45 } 46 //system("pause"); 47 return 0; 48 }