小谈记忆化搜索
引言
一般地,动态规划总要遍历全部的状态,而搜索可排除一些无效状态,特别是搜索可进行剪枝,减小空间开销。
如何协调动态规划的高效率与高消费间的矛盾呢?
一个折中的方法是记忆化搜索。在求解时,它依然按照自顶向下的顺序,只是每求解一个状态就将解保存,以后再遇到这种状态时,就不必重新求了。
--《算法竞赛宝典》
例题 洛谷P1434 滑雪
Michael喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道在一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可行的滑坡为24-17-16-1(从24开始,在1结束)。当然25-24-23―┅―3―2―1更长。事实上,这是最长的一条。
输入的第一行为表示区域的二维数组的行数R和列数C(1≤R,C≤100)。下面是R行,每行有C个数,代表高度(两个数字之间用1个空格间隔)。
输出区域中最长滑坡的长度。
1 /*注意:本题求的是最大的高度,即走几次再+1。*/ 2 #include<cstdio> 3 #include<algorithm> 4 #include<iostream> 5 #include<cmath> 6 using namespace std; 7 int n,m,ans,zx,zy; 8 int h[500][500]; 9 int f[500][500]; 10 int dx[5]={0,0,0,1,-1};//记录上下左右走的方法,注意数组从0开始作为下标 11 int dy[5]={0,1,-1,0,0}; 12 int msearch(int i,int j); 13 int main() 14 { 15 scanf("%d%d",&n,&m); 16 for(int i=1;i<=n;i++) 17 { 18 for(int j=1;j<=m;j++) 19 { 20 scanf("%d",&h[i][j]); 21 } 22 } 23 for(int i=1;i<=n;i++) 24 { 25 for(int j=1;j<=m;j++) 26 { 27 int t=msearch(i,j); 28 ans=max(ans,t);//不断比较更长的高度 29 } 30 } 31 printf("%d",ans); 32 return 0; 33 } 34 int msearch(int i,int j) 35 { 36 int t=1; 37 if(f[i][j]>0) return f[i][j];//记忆化搜索的关键之1 38 for(int k=1;k<=4;k++) 39 { 40 zx=i+dx[k]; 41 zy=j+dy[k]; 42 if(h[zx][zy]>h[i][j]&&zx>=1&&zx<=n&&zy>=1&&zy<=m)//是否满足条件&数组防越界 43 t=max(msearch(zx,zy)+1,t);//t先前设成1了(起始高度为1) 44 } 45 f[i][j]=t;//记忆化搜索关键之2,都搜完后记录。 46 return t; 47 }
昨日小测中的题目略微有所改动。
题目如下:
香穗子在田野上调蘑菇!她跳啊跳,发现自己很无聊,于是她想了一个有趣的事情,每个格子最多只能经过1次,且每个格子都有其价值
跳的规则是这样的,香穗子可以向上下左右四个方向跳到相邻的格子,并且她只能往价值更高(这里是严格的大于)的格子跳.
香穗子可以从任意的格子出发,在任意的格子结束,
那么她最多能跳几次?
本质也是记忆化搜索。另外需注意两点。
1.问的是跳几次,不同于求高度,最后需-1;
2.“每个格子最多只能通过1次”不需另做处理。