这应该是我第二次学记忆化搜索之前学过一次,但是好像什么都没学到
先来个例题
记忆化搜索,所谓记忆化搜索,就是在普通搜索的基础上增加记录,记录下每一步的最优解法,一般用于求最大值之类。在每步都记录下数值,最后函数数值即为解。
是dp的一种实现形式:一种递归的写法,递归求解每一种状态
其实每一个动态规划的问题都可以用递归的方式写,而且递归的方式回更加容易理解一些,
状态表示是两维,因为是个矩阵嘛
f[i, j]就是所有从(i, j)开始滑的路径
状态计算:按照第一步是往哪边滑的把情况分为四类
向左向右向上向下,然后以向右为例,就是从f[i, j]到f[i, j+1]然后寻找从f[i, j +1]到终点的最长距离,其实就是f[i, j+1]+1,这四类情况并不一定都存在,需要判断一下,把存在的取个遍,找个max就行
而且要求这个图是拓扑图,不能存在环 ,因为如果存在环的话,就会一直递归,谁也求不出来谁
不过本题不存在环
我们要递归地算每一个状态,一般是这么一个套路,我们先把所有的状态初始化成-1,代表每个状态都没有被算过
int &v = f[x][y]这是C++里面的一个技巧,每个写v的地方就相当于写 f[x][y]
枚举上下左右四个方向的话,用偏移量int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
#include
#include
#include
using namespace std;
const int N = 310;
int n, m;
int g[N][N];
int f[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dp(int x, int y)
{
int &v = f[x][y];
if (v != -1) return v;
v = 1;//最小值就是1,也就是就当前这个点
for (int i = 0; i < 4; i ++ )
{
int a = x + dx[i], b = y + dy[i];
if (a >= 1 && a <= n && b >= 1 && b <= m && g[x][y] > g[a][b])
v = max(v, dp(a, b) + 1);
}
return v;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &g[i][j]);
memset(f, -1, sizeof f);
int res = 0;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
res = max(res, dp(i, j));
printf("%d\n", res);
return 0;
}
优点:我们经常关注的就是时间复杂度和空间复杂度
但是考试的时候还要关注代码复杂度:也就是我们想出来的这个算法好不好写,就是我们这个算法思路给出来是不是很麻烦,一般代码复杂度最重要
虽然看着代码长一些,但是思路很简单,错了很容易调试,只要把状态转移方程写出来就可以直接实现
但是循环的话,就很难
缺点:比循环满,递归过多容易爆栈