题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1619
题目大意:找出从最左边到最右边的权值最小的一条路(起点可以是最左边的任意一点,终点同理),如果有多条,输出字典序最小的那一条的路径(只需要输出在第几行)和权值。
注意:只能向右,右上方,右下方前进,第一行可以到达最后一行,最后一行也能到第一行。
思路:动态规划,用数组来储存下一步的行数,dp记录到(i,j)的最小路径,逆向使用DP。
参考代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define INF 1e9
using namespace std;
int m, n, a[12][104], dp[12][104], b[12][104]; //m行n列,a是每点的数值,b是路径
int main()
{
while (scanf("%d%d", &m, &n) != EOF) {
memset(dp, 0, sizeof(dp));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
scanf("%d", &a[i][j]);
}
dp[i][n - 1] = a[i][n - 1];
}
int ans = INF;
for (int j = n - 1; j >= 0; --j) { //逆向使用DP
for (int i = 0; i < m; ++i) {
dp[i][j] = INF;
int j1 = j + 1;
int row[3] = { i - 1,i,i + 1 };
if (row[0] < 0)row[0] = m - 1; if (row[2] == m)row[2] = 0; //注意!
sort(row, row + 3); //使字典序最小
for (int k = 0; k < 3; ++k) {
int temp = dp[row[k]][j + 1] + a[i][j];
if (dp[i][j] > temp) {
dp[i][j] = temp;
b[i][j] = row[k];
}
}
}
}
int s; //初始行数
for (int i = 0; i < m; ++i)
if (dp[i][0] < ans) {
ans = dp[i][0];
s = i;
}
printf("%d", s + 1);
for (int i = 0; i < n - 1; ++i) {
printf(" %d", b[s][i] + 1);
s = b[s][i];
}
printf("\n%d\n", ans);
}
return 0;
}