思路:因为图还是相对而言不小的一个图,直接dfs是一定会超时的,分析题目,对于同一个点,很多走法都会走到这一个点,所以我们可以采用记忆化搜索的方法来优化算法。对于每一个已经dfs过的点的值进行记录、因为一共就有三种走法,所以状态转移方程也并不难推出:
dp【i】【j】=a【i】【j】+min(dp【i-1】【j+1】,dp【i】【j+1】,dp【i+1】【j+1】);
对于起点的确定,我们可以枚举一遍,对于终点,我们是不用多考虑的,因为在记忆化搜索的过程中就得到了最小值,然后我们维护这个最小值,并且最终确定起点。
我们拿样例1说话,dp之后对于dp数组内数据是这样的(已知起点是1,0):
16 15 13 12 12 6
0 13 20 13 11 4
0 0 12 15 13 5
0 0 10 9 6 6
0 17 11 14 10 4
不难发现,我们每一次走的点都是相对上一个点能走的三个位子里边dp值最小的那个点。
(1,1)--------->(2,2)--------->(3,3)--------->(4,4)--------->(4,5)--------->(6,6)
所以根据这样的思路,我们对应写出代码。因为我的代码写的有点挫,我们分块来研究。
首先是记忆化搜索部分:
ll dfs(int x,int y) { if(y>=m)return 0;//控制范围 //printf("%d %d\n",x,y); if(dp[x][y]==0)//如果这个点没有走过,我们走一下 { ll a,b,c; if(x-1>=0)//记得第一行和最后一行是相连的 a=dfs(x-1,y+1); else a=dfs(n-1,y+1); b=dfs(x,y+1); if(x+1<n) c=dfs(x+1,y+1); else c=dfs(0,y+1); dp[x][y]=aa[x][y]+min(a,min(b,c));//状态转移方程 return dp[x][y]; } else return dp[x][y];//如果这个点已经走过了,直接return dp的值。 }其次是对于枚举起点和初始化的部分:
int main() { while(scanf("%d%d",&n,&m)!=EOF) { for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { scanf("%I64d",&aa[i][j]); } } int pos; ll ans=0x3f3f3f; for(int i=0;i<n;i++)//枚举第一列上边全部的起点 { memset(dp,0,sizeof(dp));//每一次对应情况都不同,都应该清空dp数组 dfs(i,0); if(dp[i][0]<ans){ans=dp[i][0];pos=i;}//维护最小值 } memset(dp,0,sizeof(dp)); dfs(pos,0); dfs2(pos,0);//这次深搜是用来输出路径的 printf("%I64d\n",ans); } }最后是对于输出路径部分的控制。
需要格外注意的一个点是如果当前点能走到的三个地方的值有相同的部分,那么要输出行号相对小的。
void dfs2(int x,int y) { if(y>=m)return ; if(y!=m-1) printf("%d ",x+1); else printf("%d\n",x+1);//注意控制输出格式 ll a,b,c; int rowa,rowb,rowc; if(x-1>=0){a=dp[x-1][y+1];rowa=x-1;}//对于每一种能够走到的位子进行枚举 else {a=dp[n-1][y+1];rowa=n-1;} b=dp[x][y+1];rowb=x; if(x+1<n){c=dp[x+1][y+1];rowc=x+1;} else {c=dp[0][y+1];rowc=0;} if(a==b&&b==c)//如果有值相同的情况,进行下一层深搜的时候要行号尽量小 { dfs2(min(rowa,min(rowb,rowc)),y+1); } else if(a==b&&a<c) { dfs2(min(rowa,rowb),y+1); } else if(a==c&&a<b) { dfs2(min(rowa,rowc),y+1); } else if(b==c&&b<a) { dfs2(min(rowb,rowc),y+1); } else//如果没有值相同的情况,直接走dp值小的那个点 { if(min(a,min(b,c))==a)dfs2(rowa,y+1); if(min(a,min(b,c))==b)dfs2(rowb,y+1); if(min(a,min(b,c))==c)dfs2(rowc,y+1); } }
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; #define ll long long int ll aa[225][225]; ll dp[225][225]; int n,m; ll min(ll a,ll b) { return a>b?b:a; } ll dfs(int x,int y) { if(y>=m)return 0; //printf("%d %d\n",x,y); if(dp[x][y]==0) { ll a,b,c; if(x-1>=0) a=dfs(x-1,y+1); else a=dfs(n-1,y+1); b=dfs(x,y+1); if(x+1<n) c=dfs(x+1,y+1); else c=dfs(0,y+1); dp[x][y]=aa[x][y]+min(a,min(b,c)); return dp[x][y]; } else return dp[x][y]; } void dfs2(int x,int y) { if(y>=m)return ; //printf("%d %d\n",x+1,y+1); if(y!=m-1) printf("%d ",x+1); else printf("%d\n",x+1); ll a,b,c; int rowa,rowb,rowc; if(x-1>=0){a=dp[x-1][y+1];rowa=x-1;} else {a=dp[n-1][y+1];rowa=n-1;} b=dp[x][y+1];rowb=x; if(x+1<n){c=dp[x+1][y+1];rowc=x+1;} else {c=dp[0][y+1];rowc=0;} // printf("row:%d %d %d\n",rowa,rowb,rowc); //printf(" %I64d %I64d %I64d\n",a,b,c); if(a==b&&b==c) { //printf("%d %d %d\n",rowa,rowb,rowc); dfs2(min(rowa,min(rowb,rowc)),y+1); } else if(a==b&&a<c) { dfs2(min(rowa,rowb),y+1); } else if(a==c&&a<b) { dfs2(min(rowa,rowc),y+1); } else if(b==c&&b<a) { dfs2(min(rowb,rowc),y+1); } else { if(min(a,min(b,c))==a)dfs2(rowa,y+1); if(min(a,min(b,c))==b)dfs2(rowb,y+1); if(min(a,min(b,c))==c)dfs2(rowc,y+1); } } int main() { while(scanf("%d%d",&n,&m)!=EOF) { for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { scanf("%I64d",&aa[i][j]); } } int pos; ll ans=0x3f3f3f; for(int i=0;i<n;i++) { memset(dp,0,sizeof(dp)); dfs(i,0); if(dp[i][0]<ans){ans=dp[i][0];pos=i;} } memset(dp,0,sizeof(dp)); dfs(pos,0); dfs2(pos,0); printf("%I64d\n",ans); } }