题目
思路
多阶段图的最短路
1.本题中,每一列就是一个阶段,每个阶段都有3种决策:直行,右下和坐上。
2.状态定义:(i,j),出于第j列第i行。
3.指标函数:d(i,j),从格子(i,j)出发到最后一列的最短边权路。(最短边权路,是将格子数字大小作为边的权,即数字之和最小的路)
4.状态转移方程:
5.对于多阶段决策问题里,由于阶段就是天然的顺序,所以递推就好,没必要再用记忆化搜索。
6.本题输出解的时候,采用“递推顺带记录”的方法。
代码
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b); i++)
#define _rep(i,a,b) for(int i = (a); i<=(b); i++)
using namespace std;
const int INF = 1 << 30;
const int maxn = 100 + 10;
int n, m, A[maxn][maxn], d[maxn][maxn], mynext[maxn][maxn];
// n:行数, m:列数
int main() {
while (scanf("%d%d", &n, &m) == 2) {
_rep(i, 1, n)
_rep(j, 1, m)
scanf("%d", &A[i][j]);
int ans = INF, first = 0;
for(int j = m-1; j>=0; j--) // 阶段
_rep(i, 1, n) {
if (j == m - 1) d[i][j] = A[i][j+1];
else {
int rows[3] = { i,i - 1,i + 1 };
if (i == 1) rows[1] = n; // 因为矩阵是环形的
if (i == n) rows[2] = 1;
sort(rows, rows + 3); // 因为要按字典序输出
d[i][j] = INF;
_for(k, 0, 3) {
int v = d[rows[k]][j + 1] + A[i][j+1];
if (v < d[i][j]) {
d[i][j] = v;
mynext[i][j] = rows[k];
}
}
}
if (j == 0 && d[i][j] < ans) {
ans = d[i][j];
first = i;
}
}
printf("%d", first);
for (int i = mynext[first][0], j = 1; j < m; i = mynext[i][j], j++)
printf(" %d", i);
printf("\n%d\n", ans);
}
return 0;
}