链接 : https://vjudge.net/problem/UVA-116
题意:给定m行n列(m<=10,n<=100)的整数矩阵,从第一列的任何一个位置出发每次往右或右上或右下走一格,最终到达最后一列。要求经过的整数之和最小。整个矩阵是环形的,即 第一行的上一行是最后一行,最后一行 的下一行是第一行。输出路径上的每列的行号。多解时输出字典序最小的。
分析:题目是紫书上的,P270. LRJ大神把这个分在 多阶段决策中的多段图的最短路问题中。
因为在本题中,每个阶段就是每一列,每个阶段都有三种决策:直行,右上,右下。
下面是我的理解,在本题中,因为终态是 最后一列,初态是 第一列。
DP做法,n,m的范围不大,设dp[i][j]: 从格子(i,j)出发到 最后一列的 最小整数和。 这种假设方法可以理解为最后一列为初态, 那么循环采用逆推的方法枚举列。
/* 若把dp[i][j] 设为: 从 第一列出发 到 最后一列的最小整数和,那么就顺推 枚举*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=105;
const int INF=0x3f3f3f3f;
int a[N][N],dp[N][N];
int n,m,ne[N][N];
void solve()
{
int first=0;
for(int j=n-1; j>=0; j--)///逆推 枚举列
{
for(int i=0; i<m; i++)///枚举行
{
if(j==n-1) dp[i][j]=a[i][j];///边界: 到达最后一列
else
{
dp[i][j]=INF;///初始时 是一个不可能达到的值
int rows[]= {i,i-1,i+1};///三种决策
if(i==0) rows[1]=m-1;/// 第1行上面是最后一行
if(i==m-1) rows[2]=0;/// 最后一行下面是第一行, 符合题中 矩阵是环的规则
sort(rows,rows+3); ///对rows 重新排序,找到字典序最小
for(int k=0; k<3; k++)///通过对三种决策的试探,找到最小的那一个
{
int val=a[i][j]+dp[rows[k]][j+1];
if(dp[i][j]>val)///当前状态比下一个状态的 整数和要大
{
dp[i][j]=val;
ne[i][j]=rows[k];
}
}
}
if(j==0&&dp[i][j]<dp[first][j])///第一列
first=i;///first记录第一列的行号
}
}
printf("%d",first+1);
int j=1;
for(int i=ne[first][0]; j<n; i=ne[i][j],j++)
///ne记录行号,此处实现类似链表,因为循环中rows的处理,此处的输出一定是字典序最小(最小整数和相等的情况下)
printf(" %d",i+1);
printf("\n%d\n",dp[first][0]);////dp[first][0]就是题目要求的最小整数和
}
int main()
{
while(~scanf("%d%d",&m,&n))
{
for(int i=0; i<m; i++)
for(int j=0; j<n; j++)
scanf("%d",&a[i][j]);
solve();
}
return 0;
}
题目读起来 很长,感觉啰嗦,有点迷糊,但是抓住题目的关键点: 起始在第一列的任意位置,矩阵是环形的,终态在最后一列,要求最小整数和, 那么是最短路问题,结合数据范围,可以采用DP,要求输出行号(字典序顺序)。用数组记录行号 感觉还是有迹可循,但是这还是清楚了之火才这样觉得。。