1.代码
#include<iostream>
using namespace std;
int m,n,ans,a[110][110],s[110][110];
int dfs(int x,int y)
{
int k;
int tx,ty;
int next[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
if (s[x][y])
return s[x][y];
s[x][y]=1;
for (k=0;k<4;k++)
{
tx=x+next[k][0];
ty=y+next[k][1];
if (tx<1 || tx>n || ty<1 || ty>m)
continue;
if (a[tx][ty]<a[x][y])
{
dfs(tx,ty);
s[x][y]=max(s[x][y],s[tx][ty]+1);
}
}
return s[x][y];
}
int main()
{
int i,j;
cin>>n>>m;
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
cin>>a[i][j];
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
ans=max(ans,dfs(i,j));
cout<<ans;
return 0;
}
2.分析
(1)记忆化+递归是实现dp的一种方式,其本质是dfs,而区别于普通的递归+回溯的dfs,两者有一个明显的不同点:函数的定义。记忆化定义dfs:int dfs( );递归回溯定义dfs:void dfs ( )。记忆化会有返回值,而递归回溯不会有返回值。
产生这样的区别的原因是,记忆化会在dfs函数内有一个判断,若某个点已搜过,则直接返回之前搜索的答案,而这里是有返回数据的,因此记忆化要用int定义。
(2)在记忆化dfs中,有多个return,有些返回数值,有些不返回,有些返回的同时还要记录数组值,那这些return分别是什么含义呢?
·第一处return
if (s[x][y])
return s[x][y];
显然,返回记忆值,而返回到的地方是上一级dfs函数,如下位置
if (a[tx][ty]<a[x][y])
{
dfs(tx,ty); //返回到这里
s[x][y]=max(s[x][y],s[tx][ty]+1);
}
·第二处return
int dfs( )
{
...
...
...
return s[x][y];
}
此处是某级dfs函数结束,已经运行完了所有步骤,那返回到哪呢?
和上面第一处return返回的位置一样,还是返回到上一级dfs。
if (a[tx][ty]<a[x][y])
{
dfs(tx,ty); //返回到这里
s[x][y]=max(s[x][y],s[tx][ty]+1);
}
只要一个函数中出现return,永远是返回到最近一次调用它的地方。
如果在第一个return没能返回,那么就在第二个return返回。
#注意此处不能这样写
if (a[tx][ty]<a[x][y])
dfs(tx,ty);
return s[x][y]=max(s[x][y],s[tx][ty]+1);
}
虽然在return的同时记忆数组值是一种很常见的写法,但在这里是错误的。
return s[x][y]=max(s[x][y],s[tx][ty]+1);
==
s[x][y]=max(s[x][y],s[tx][ty]+1);
return s[x][y];
在这里s[x][y]不是四个方向都去,而是要在四个方向中挑选一个走,因为最终只能走出来一条路,s[x][y]取max必须放在for循环里,因此这里不能简写成在return的同时记忆数组值。
(3)主函数中第一次调用dfs函数,在第一级dfs函数末尾的return会返回一个数值到主函数中,因此需要用一个变量来记录返回值,例如 ans=dfs(...)。
而递归回溯则不需要,因为它用的是全局变量,且没有返回值。
(4)细节
从某个点开始,就算一步也不走,答案也是1。
if (s[x][y])
return s[x][y];
s[x][y]=1; //一定要注意初始化
for (k=0;k<4;k++)
3.结语
理解dfs,知道下一步将走向哪里,最好的方法就是通过调试看程序是如何运行每一行代码,下一步将走向哪里,只有这样才能真正学通dfs。