分析:给一个m行n列 (m≤10,n≤100) 的整数矩阵,从第一列任何一个位置出发每次往右、右上或者右下走一格,最终到达最后一列。要求经过的整数之和最小。整个矩阵式是环形的,即第一行的上一行是最后一行,最后一行的下一行是第一行。输出路径上每列的行号。多解时输出字典序最小的。如图是两个矩阵和对应的最优路线(唯一的区别是最后一行)。(本段摘自《算法竞赛入门经典(第2版)》)
分析:
dp[i][j]为从格子(i,j)出发到最后一列的最小开销。状态转移时要注意字典序最小的情况。
代码:
#include <iostream>
#include <algorithm>
#include <fstream>
#include <string>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <cctype>
#include <stack>
#include <set>
using namespace std;
const int maxn = 100 + 5, INF = 1000000000;
int n, m, ans, first;
int a[maxn][maxn], dp[maxn][maxn], NEXT[maxn][maxn];
int main()
{
while (~scanf("%d%d", &n, &m))
{
ans = INF;
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
scanf("%d", &a[i][j]);
for (int j = m - 1; j >= 0; --j)
for (int i = 0; i < n; ++i)
{
if (j == m - 1)
dp[i][j] = a[i][j];
else
{
int row[3] = {i, (i - 1 + n) % n, (i + 1) % n};
sort(row, row + 3);
dp[i][j] = INF;
for (int k = 0; k < 3; ++k)
{
int tmp = dp[row[k]][j + 1] + a[i][j];
if (tmp < dp[i][j])
{
dp[i][j] = tmp;
NEXT[i][j] = row[k];
}
}
}
if (j == 0 && dp[i][j] < ans)
{
ans = dp[i][j];
first = i;
}
}
printf("%d", first + 1);
for (int i = NEXT[first][0], j = 1; j < m; i = NEXT[i][j], ++j)
printf(" %d", i + 1);
printf("\n%d\n", ans);
}
return 0;
}