一. Vjudge链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?OJId=UVA&probNum=116
二. 题目大意:给一个矩阵,让你从左边走到右边,每次只能走右,右上,右下。从第一行走右上到最后一行,从最后一行走右下到第一行,求最小的路径长度(每次走过一个格子,路径增加格子里面的数),并输出最小字典序的路径(从第一列到最后一列关于行的路径)。
三. 思路:本来以为是DP水题,3个状态,上中下取最小,扫一遍就完了,没想到它弄了个字典序最小输出,这里指的是先比较第一个,再比较第二个。因此一定要从后向前扫,每次取最小,不能从前向后扫。(这个不知道为什么,留待以后补充。)因为它是环形的,2条路最小值一样,其中一条全部往右走,如果有一条路从第一个跳到了最后一个的话,从前往后扫会取那条中间的路,但是这样是错误的,因为会比较先输出的,因此要取那条跳到最后一个的路。
四. 代码:
#include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <cmath> #include <algorithm> #include <stack> using namespace std; const int MAX_N = 128, MAX_M = 15, INF = 0x3f3f3f3f; int dp[MAX_M][MAX_N], row, col, Next[MAX_M][MAX_N]; int getMin(int i, int j) { j = j+1;//注意我一进入就加上1,以后不用重复写 int up, mid, down, idx, res; mid = i; up = (i-1 == 0? row:i-1); down = (i+1 > row? 1:i+1); idx = INF; if(dp[mid][j] <= dp[up][j] && dp[mid][j] <= dp[down][j]) res = dp[mid][j], idx = min(idx, mid); if(dp[up][j] <= dp[mid][j] && dp[up][j] <= dp[down][j]) res = dp[up][j], idx = min(idx, up); if(dp[down][j] <= dp[mid][j] && dp[down][j] <= dp[up][j]) res = dp[down][j], idx = min(idx, down); Next[i][j-1] = idx; return res; } int main() { //freopen("in.txt", "r", stdin); int i, j; while(~scanf("%d %d", &row, &col)){ for(i = 1; i <= row; i++) for(j = 1; j <= col; j++) scanf("%d", &dp[i][j]); for(j = col-1; j >= 1; j--) for(i = 1; i <= row; i++) dp[i][j] += getMin(i, j); int res = INF, idx; for(i = 1; i <= row; i++) if(res > dp[i][1]) res = dp[i][1], idx = i; printf("%d", idx); for(i = 1; i < col; i++){ idx = Next[idx][i]; printf(" %d", idx); } printf("\n%d\n", res); } return 0; }