题意:给一个n行m列的矩阵,要求从第 1 列走到第 m 列,路过每个点时将该点的权值加起来。从当前点走向下一点只能走右上、右、右下4个方向,并且图是环状的,从1向右上走会走到n,同理n向右下会走到1。求最小权值和,并输出路径,若权值相等,输出字典序小的那一种情况。
思路:紫书上的题,紫书上用递推的dp,我写了记忆化搜索的方法。实质是一样的,每次dfs保存当前节点的下一个最有路径。再通过 way[dir][i]访问。
#include <iostream>
#include <cstdio>
#include <cstring>
#define INF 0x3fffffff
using namespace std;
typedef long long ll;
int n,m;
int dp[110][110];
int maze[110][110];
int way[110][110];
int dfs(int i,int j){
int res = INF,dir = INF;
if(dp[i][j]!=-1) return dp[i][j];
else if(j == m ){
return maze[i][j];
}
else{
int dire[3];
if(i == 1)
dire[0] = n,dire[1] = i,dire[2] = i+1;
else if(i == n)
dire[0] = i-1,dire[1] = i,dire[2] = 1;
else
dire[0] = i-1,dire[1] = i,dire[2] = i+1;
for(int t =0;t<3;t++){
int nt = dfs(dire[t],j+1) + maze[i][j];
if(res > nt)
dir = dire[t],res = nt;
else if(res == nt)
dir = min(dir,dire[t]);
}
}
way[i][j] = dir;
return dp[i][j] = res;
}
int main()
{
freopen("in.txt","r",stdin);
while(~scanf("%d%d",&n,&m)){
for(int i =1;i<=n;i++)
for(int j =1;j<=m;j++) scanf("%d",&maze[i][j]);
int ans = INF,dir;
if(n == 1){
ans = 0;char ch;
for(int i=1;i<=m;i++){
ans+=maze[1][i];ch = i==m?'\n':' ';
printf("%d%c",1,ch);
}
printf("%d\n",ans);
continue;
}
memset(dp,-1,sizeof dp);
for(int i =1;i<=n;i++){
int now = dfs(i,1);
if(now < ans){ans = now,dir = i;}
}
for(int i =1;i<=m;i++){
char ch = i==m?'\n':' ';
printf("%d%c",dir,ch);
dir = way[dir][i];
}
printf("%d\n",ans);
}
return 0;
}