题目:给你一个n*m的数字表格,找到一条从左到右的路径,使得上面的数字和最小。输出字典序最小的行号路径
(每次可以从(i,j),走到(i,j+1),(i+1,j),(i-1,j)循环无限延伸没有边界)
思路:由于要输出字典序最小的路径,所以要逆向dp,边界在n列从右到左dp
说明:逆向dp保证字典序最小(后继最小),正向能保证每点前驱最小。
//0 KB 139 ms
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int r,c;
int mapp[15][150];
int next[15][150];
int dp[15][150];
int main()
{
while(~scanf("%d%d",&r,&c)){
memset(dp,0x3f,sizeof(dp));
memset(next,0,sizeof(next));
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++){
scanf("%d",&mapp[i][j]);
}
int ans=inf,head;
for(int i=1;i<=r;i++){
dp[i][c]=mapp[i][c];
if(c==1){ //这里wa哭我了,忘记了初始化边界时候,列数可能就是1行,所以边界就是答案,所以初始化边界时候也要确定ans的值
if(ans>dp[i][c]){
ans=dp[i][c];
head=i;
}
}
}
for(int i=c-1;i>0;i--)
for(int j=1;j<=r;j++){
int row[3];
row[0]=j;
row[1]=(j==r? 1: j+1 );
row[2]=(j==1? r: j-1 ) ;
sort(row,row+3);
dp[j][i]=inf;
for(int k=0;k<3;k++){
if(dp[j][i]>dp[row[k] ][i+1]+mapp[j][i] ){
dp[j][i]=dp[row[k] ][i+1]+mapp[j][i];
next[j][i]=row[k];
}
}
if(i==1){
if(ans>dp[j][i]){
ans=dp[j][i];
head=j;
}
}
}
int path=head;
printf("%d",path);
for(int i=1;i<c;i++){
path=next[path][i];
printf(" %d",path);
}
printf("\n%d\n",ans);
}
return 0;
}