9.3.1多阶段决策问题 - 多段图的最短路

例题 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 }

 

转载于:https://www.cnblogs.com/DrCharlie/p/Ex9_4-UVa116.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值