题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=114&page=show_problem&problem=52
题目大意:给定一个n*m的矩阵,问从第一列走到最后一列经过的最小路径长度,方向有三个:右上,右,右下,第一行和最后一行是连通。在输出最小路径的同时输出经过哪些行,按字典序最小输出。
解题思路:记忆化搜索,模型很简单,就是从底向上,每次都找三个方向的最小值,关键是要输出路径,我是用一个path数组保存选择的方向。
测试数据:
3 31 2 3
3 7 2 8 6 4
3 7 2 1 2 3
代码:
#include <stdio.h> #include <string.h> #define MIN 40 #define MAX 200 #define INF 1000000000 int dir[3][2] = {{-1,1},{0,1},{1,1}}; int n,m,arr[MIN][MAX],vis[MIN][MAX]; int dp[MIN][MAX],path[MIN][MAX],minn,mini; int Geti(int i) { if (i > n) i = 1; if (i < 1) i = n; return i; } int Dfs(int i,int j) { if (vis[i][j]) return dp[i][j]; vis[i][j] = 1; if (j == m) { dp[i][j] = arr[i][j]; return dp[i][j]; } int tpmin = INF,tp; for (int k = 0; k < 3; ++k) { int ii = Geti(i + dir[k][0]); int jj = j + dir[k][1]; tp = Dfs(ii,jj) + arr[i][j]; if (tp < tpmin || tp == tpmin && ii < Geti(i + dir[path[i][j]][0])) { path[i][j] = k; tpmin = tp; dp[i][j] = tpmin; } } return dp[i][j]; } int main() { int i,j,k,tpi,tpj; while (scanf("%d%d",&n,&m) != EOF) { for (i = 1; i <= n; ++i) for (j = 1; j <= m; ++j) scanf("%d",&arr[i][j]); for (i = 1; i <= n; ++i) for (j = 1; j <= m; ++j) vis[i][j] = 0,dp[i][j] = path[i][j] = INF; minn = INF; for (i = 1; i <= n; ++i) { Dfs(i,1); if (dp[i][1] < minn) minn = dp[i][1],mini = i; } printf(m == 1 ? "%d\n" : "%d ",mini); tpi = mini,tpj = 1; for (i = 1; i < m; ++i) { k = path[tpi][tpj]; tpi = Geti(tpi + dir[k][0]); tpj += dir[k][1]; printf(i == m - 1 ? "%d\n" : "%d ",tpi); } printf("%d\n",minn); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。