https://vjudge.net/contest/279505#problem/C
题目大意:
给一个n*m的矩阵 数字代表值,有三个方向移动,向右,右上,右下。可以从n行到第1行,也可以从第1行到第n行。求从第一列到最后一列所经过路程值的和,并打印路径(行数),要求字典序最小。
题目分析:
因为要求字典序最小,正推不好记录路径。所以采用逆推。
dp【i】【j】表示:第i行第j列得最小值。
dp【i】【j-1】=min(dp【i】【j-1】,dp【i-1】【j】+a【i-1】【j】,dp【i】【j】+a【i】【j】,dp【i+1】【j】+a【i+1】【j】);
枚举第一行得最小值便是答案。
路径记录难点:在更新值后,path【row【k】】【j-1】记录从第j列的哪一行来的。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int N=110;
int dp[N][N],a[N][N],path[N][N];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
memset(dp,INF,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
if(j==m)dp[i][m]=a[i][m];
}
}
for(int j=m;j>1;j--){
for(int i=1;i<=n;i++){
int row[3]={i-1,i,i+1};
if(i==1)row[0]=n;
if(i==n)row[2]=1;
sort(row,row+3);
for(int k=0;k<3;k++){
int tmp=dp[i][j]+a[row[k]][j-1];
if(tmp<dp[row[k]][j-1])
{
dp[row[k]][j-1]=tmp;
path[row[k]][j-1]=i;
}
}
}
}
int p=INF,r;
for(int i=1;i<=n;i++){
if(p>dp[i][1]){
r=i;
p=dp[i][1];
}
}
printf("%d",r);
for(int i=path[r][1],j=1;j<m;j++,i=path[i][j])
printf(" %d",i);
printf("\n%d\n",p);
}
}