刷题笔记(2)

记忆化搜索

记忆化搜索,其实是动态规划的一种实现形式。
但是记忆化搜索虽然需要一个足够大的矩阵空间,但通过剪枝有时可以省下时间。而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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值