再探记忆化(深度解析)——洛谷P1434

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。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值