解题思路:联系回溯法,使用阶段的思路,每一列相当于层数,可转移的三个状态为三棵子树,因此用一个二维数组dp[i][j]表示状态,其中j代表阶段(层数),在这里就是第几列,i表示行数。注意矩阵为环形,要做相应的处理,深入理解阶段的含义以及动态规划和回溯的关系。细节参见代码。
题目大意:从矩阵第一列的任一位置出发往右,可以直接往右,往右上或往右下,最终到达最后一列,整个矩阵为环形,第一行的上一行为最后一行,最后一行的下一行为第一行,要求经过的整数和最小,求最优路线。多解时输出字典序最小的。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 1<<30
int maze[11][101];
int dp[11][101];
int n,m;
void print(int x,int y) //打印字典序最小的路径
{
cout<<x<<" ";
if(y==m) {
cout<<endl;
return;
}
int row[3]={x-1,x,x+1};
if(x==1) row[0]=n; //注意这里的环形处理
if(x==n) row[2]=1;
sort(row,row+3); //因为要求输出字典序最小的,这里需要重新排序
for(int i=0;i<=3;i++)
{
if(dp[x][y]==dp[row[i]][y+1]+maze[x][y])
{
print(row[i],y+1);
break;
}
}
}
int main()
{
while(cin>>n>>m)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>maze[i][j];
}
}
for(int i=1;i<=n;i++) //边缘条件,到达最左边后需要加的整数
{
dp[i][m]=maze[i][m];
}
for(int j=m-1;j>=1;j--)
{
for(int i=1;i<=n;i++)
{
dp[i][j]=inf;
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++)
{
dp[i][j]=min(dp[i][j],dp[row[k]][j+1]+maze[i][j]);
}
}
}
int ans=inf,st=0;
for(int i=1;i<=n;i++)
{
if(dp[i][1]<ans)
{
ans=dp[i][1];
st=i;
}
}
print(st,1);
cout<<ans<<endl;
}
return 0;
}