记忆化搜索笔记
集训时一不小心透露了自己不懂记忆化搜索,于是就。。
概述
记忆化搜索采用的是搜索,在求解时从顶向下求解,每求解一个状态,就将它的解保存下来,在之后遇到重复的状态时,可以不必重新求解。简单来说,就是我们记录一下遇到的每一个状态的值。
特点
全局最优,一定会用一个数组或其他存储结构存储之前得到的子问题的解(空间换时间)
适用范围
必须是分步计算,且搜索过程中的一个搜索的结果必须建立在同类型问题的基础上(分治)当一个题目根据具体判断可能会出现重复的答案时候,或是不加以优化的暴力搜索会超时的时候极其适合
思想
根据动态规划方程写出递归式,下函数的开头直接返回以前计算过的结果(需要一个存储结构来记录之前算过的结果)
下面是我被题目虐时间
1.洛谷经典function(P1464)
题目:
对于一个递归函数w(a,b,c)w(a,b,c)
如果a≤0 or b≤0 or c≤0就返回值11
如果a>20 or b>20 or c>20就返回w(20,20,20)
如果a<b并且b<c 就返回w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c)w(a,b,c−1)+w(a,b−1,c−1)−w(a,b−1,c)
其它的情况就返回w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1)w(a−1,b,c)+w(a−1,b−1,c)+w(a−1,b,c−1)−w(a−1,b−1,c−1)
输入格式
会有若干行,并以-1,-1,-1−1,−1,−1结束。
保证输入的数在[-9223372036854775808,9223372036854775807][−9223372036854775808,9223372036854775807]之间,并且是整数。
输出格式
输出若干行,每一行格式:w(a, b, c) = ans
注意空格。输入格式,会有若干行,并以-1,-1,-1−1,−1,−1结束。
热爱暴力的我先上了我的传统艺能:
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
ll f[30][30][30];
int w(ll a, ll b, ll c){
if(a<=0||b<=0||c<=0){
return 1;
}
else if(a>20||b>20||c>20){
return w(20,20,20);
}
else if(a<b&&b<c){
return w(a,b,c-1) + w(a,b-1,c-1) - w(a,b-1,c);
}
else{
return w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
}
}
int main(){
ll a, b, c;
while(1){
cin >> a >> b >> c;
if(a==-1&&b==-1&&c==-1) return 0;
else
printf("w(%lld, %lld, %lld) = %d\n", a, b, c, w(a, b, c));
}
return 0;
}
结果你懂得:

我枯了....
绞尽脑汁想出来一个办法:
记忆化搜索!!
开辟一个数组 p[][][],用来存储计算出来的结果。
因为题目中给出了一个条件 “ 如果 a>20 or b>20 or c>20就返回w(20,20,20) ” 那么数组只要最小开到 f[21][21][21]就够用了。先用for跑一遍把数据先存储到数组内,以后调用就不用递归了!!
代码:
#include<bits/stdc++.h>
using namespace std;
int p[21][21][21];
int a,b,c;
int main()
{
for (int i=0;i<=20;++i)
for (int j=0;j<=20;++j)
for (int k=0;k<=20;++k)
p[i][j][k]=1;
for (int i=1;i<=20;++i)
for (int j=1;j<=20;++j)
for (int k=1;k<=20;++k)
p[i][j][k]=p[i-1][j][k]+p[i-1][j-1][k]+p[i-1][j][k-1]-p[i-1][j-1][k-1];
for (;;)
{
scanf("%d%d%d",&a,&b,&c);
if (a==-1&&b==-1&&c==-1)
return 0;
if (a<=0||b<=0||c<=0)
{
printf("w(%d, %d, %d) = %d\n",a,b,c,1);
continue;
}
if (a>20||b>20||c>20)
{
printf("w(%d, %d, %d) = %d\n",a,b,c,p[20][20][20]);
continue;
}
printf("w(%d, %d, %d) = %d\n",a,b,c,p[a-1][b][c]+p[a-1][b-1][c]+p[a-1][b][c-1]-p[a-1][b-1][c-1]);
}
return 0;
}
2.POJ 1088 滑雪
Problem Description
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。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。
Input
输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。
Output
输出最长区域的长度。
Sample Input
5 5
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
Sample Output
25
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int map[110][110],f[110][110],n,m;
int dx[5]={0,0,1,-1};
int dy[5]={1,-1,0,0};
int dfs(int x,int y) {
if(f[x][y]!=-1)
return f[x][y];
f[x][y]=0;
int sum=0;
for(int i=0;i<5;i++)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&map[x][y]>map[xx][yy])
sum=max(sum,dfs(xx,yy));
}
return f[x][y]=sum+1;
}//通过存储每个数组的最优解实现记忆化
int main() {
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>map[i][j];
memset(f,-1,sizeof(f));
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int x=dfs(i,j);
ans=max(ans,x);
}
cout<<ans<<endl;
return 0;
}

浙公网安备 33010602011771号