记忆化搜索
记忆化搜索,其实是动态规划的一种实现形式。
但是记忆化搜索虽然需要一个足够大的矩阵空间,但通过剪枝有时可以省下时间。而DP可以通过状压等手段节省空间。一般情况下两者都可以使用。
滑雪
以滑雪为例
//滑雪的两种方法对比
#include <iostream>
#include <cstdio>
using namespace std;
int map[105][105];
bool vis[105][105];
int table[105][105];
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };
int r, c;
bool isOK(int x, int y,int h) {
if (x >= 1 && x <= r && y >= 1 && y <= c && !vis[x][y] && h > map[x][y]) {
return true;
}
return false;
}
int dfs(int x, int y) {
if (table[x][y] != 0) {
return table[x][y];
}
vis[x][y] = 1;
int ans = 1;
int opt = 0;
for (int i = 0; i < 4; i++) {
if (isOK(x + dx[i], y + dy[i], map[x][y])) {
int tmp = dfs(x + dx[i], y + dy[i]);
opt = max(opt,tmp);
}
}
ans += opt;
vis[x][y] = 0;
table[x][y] = ans;
return ans;
}
int main()
{
cin >> r >> c;
for (int i = 1; i <= r; i++) {
for (int j = 1; j <= c; j++) {
cin >> map[i][j];
}
}
int ans = 0;
for (int i = 1; i <= r; i++) {
for (int j = 1; j <= c; j++) {
int tmp = dfs(i, j);
ans = max(tmp, ans);
}
}
cout << ans;
}
总结一下记忆化搜索的模板与DFS没本质区别
只不过是在搜索前判断是否已有答案,以及在结束递归的时候存已知的答案。其实难点是如何确定状态及状态转移方程(也是DP的难点)。
出栈顺序
栈
这个题除了用卡特兰数做之外,最容易想到的是记忆化搜索了。
状态确定为f[i][j],i为输入队列中剩余的个数,j为栈中的个数``。两个状态确定了答案。
1)i=0 , return 1
此时全在栈中
2)j=0 return f[i-1][j+1]
此时必须从队列中进栈
3) return f[i][j-1]+f[i-1][j+1]
此时 要么弹栈,要么从队列进栈。
ll ans[20][20];
ll dfs(int i, int j) {
if (ans[i][j] != 0) {
return ans[i][j];
}
ll ret = 0;
if (i == 0) {
ret = 1;
}
else if (j == 0) {
ret = dfs(i - 1, j + 1);
}
else {
ret = dfs(i - 1, j + 1) + dfs(i, j - 1);
}
ans[i][j] = ret;
return ret;
}
A*搜索
A*搜索也叫启发式搜索,现在太菜,只会很简单的应用。
核心是:F(x) = g(x) + h(x)
F(x) 是代价函数,g(x)是从初始状态到x所耗费的代价,常用欧氏距离。h(x)是预估代价,即为从x到目标状态预估的代价,常用曼哈顿距离。
八数码
一看到题目,很自然的想法就是BFS,但是很有可能超时。
建议看下B站的教学视频和这个博客
我写的完整代码(很菜勿喷)
关于用字符串表示状态:
比如二维数组状态为
1 2 3
4 5 6
7 8 9
我们就用"123456789"来表示。
bool bfs(Node s) {
myMap[s.str] = 1;
openList.push(s);
while (!openList.empty()) {
Node cur = openList.top();//openList是优先队列,以cost(代价函数)从小到大
openList.pop();
if (isEnd(cur.str)) {//终止条件:到达目标状态
closeList.push(cur);//closeList是栈,便于记录之前的路径
printf("最短需要%d步移动\n", cur.step);
print();
return true;
}
for (int i = 0; i < 4; i++) {//这里i代表着枚举移动方向,具体看完整代码
int x = getx(cur.x, i); int y = gety(cur.y, i);
if (isBound(x, y))
continue;
string str = getstr(cur.x, cur.y, i, cur.str);
if (!myMap[str]) {//重点采用的是MAP去重,每一个状态代表一个字符串。
Node tmp = node(cur.x, cur.y, x, y, i, cur.step, str);
myMap[str] = 1;//标记
openList.push(tmp);//出队(BFS惯例)
}
}
closeList.push(cur);//入栈便于回溯路径
}
return false;
}