王道机试 第九章 搜索

第九章 搜索01

在这里插入图片描述

如果求最低话费的路径,则四要素转变为在这里插入图片描述

搜索问题:起始状态经过一系列的状态转移抵达目标状态

Search Trees–宽度优先搜索(BFS)& 深度优先搜索(DFS)

宽度优先搜索(BFS)-- 队列

例题王道9.1:
在这里插入图片描述
分析:
状态空间(位置n,时间t)
状态转移 (n-1.t+1),(n+1,t+1),(n*2,t+1)
起始状态(N,0)
目标状态(K ,lowest time)
分析:

  • 使用宽度优先搜索,
  • 每一层是相同的时间 并且为防止访问同样的点浪费程序时间:可以设置一个visit数组

在这里插入图片描述

#include <iostream>
#include <cstdio>
//因为使用的是宽度优先搜索,所以需要用到队列这个数据结构,所以需要加上:
#include <queue>
//memset使用需要加上
#include <cstring>

using namespace std;

//题目要求的是100000
const int MAXN = 1e5 + 10;

//需要用一个结构体来存储状态,注意结构体后面要加分号
struct Status {
    int position;
    int time;
    Status(){}
    Status(int p,int t):position(p), time(t){}
};

//定义一个访问数组
bool visit[MAXN];

//写一个宽度优先搜索的函数
int BFS(int n, int k) {
    //定义一个存放状态的队列
    queue<Status> myQueue;
    myQueue.push(Status(n,0));      //注意压入队列的写法
    visit[n] = true;
    //如果队列非空就
    while(!myQueue.empty()) {
        Status current = myQueue.front();
        if(current.position == k){
            return current.time;
        }
        //除此之外就是没找到的状态
        myQueue.pop();
        //转入新的状态,使用状态转换方程
        for(int i = 0; i < 3; i++){
            Status next = current;
            if(i == 0) {
                next.position -= 1;
            } else if(i == 1){
                next.position += 1;
            } else {
                next.position *= 2;
            }
            next.time += 1;
            //如果下一个位置不合法,则跳过(不对这个状态进行扩展);为了提高搜索性能
            if(next.position < 0 || next.position > 1e5 || visit[next.position]){
                continue;
            }
            //若合法 或者 没有访问过的状态,则压入队列
            myQueue.push(next);
            visit[next.position] = true;

        }
    }
}

int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    memset(visit, false, sizeof(visit));
    //主函数中调用这个函数即可
    printf("%d\n", BFS(n,k));
    return 0;
}

例题9。2:
在这里插入图片描述

分析:
状态空间:n*k
状态转移:n*k + n
起始状态:  n
目标状态:n * k 由 01构成

上诉方法的状态空间过大,可以反过来思考:找01构成的数,反过来看是否是n的倍数,效率高很多

状态空间:01构成的number
状态转移:(number*10,(number*10 + 1)
起始状态:1
目标状态:number % n ==  0                                                                                                                  

Search tree描述:
在这里插入图片描述
由于不会出现重复的节点数值,所以不需要设置visit数组
BFS

代码:

#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;

//定义BFS
void BFS(int n){
    queue<long long> myQueue;           //队列的声明,尖括号中是数据类型
    myQueue.push(1);
    while(!myQueue.empty()) {
        long long current = myQueue.front();
        if(current % n == 0){
            printf("%lld\n",current);     //long long输出需要lld!!!!!
            return;
        }
        myQueue.pop();
        /*转入新的状态,使用状态转换方程:
        1.使用循环,有几个转换方程循环几次,用if(i==循环次数)进行状态转移
        2.考虑是否合法或有无访问过的状态
        3。压入队列
        */
        for(int i = 0; i < 2; i++){
            if(i == 0){
                myQueue.push(current * 10);
            }
            if(i == 1){
                myQueue.push(current * 10 + 1);
            }
        }
    }
}

int main(){
    int n;
    while (scanf("%d",&n) != EOF){
        if(n == 0){
            break;
        }
        BFS(n);
    }
    return 0;
}

遇到的错误点:wrong answer --输出long long 需要用到%lld

第九章 搜索02

深度优先搜索DFS – 栈 或 递归的方式不用栈

在这里插入图片描述
例题9.3:题目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意:
1.因为层数==遍历走过的格子数,所以优先深度遍历
2.根据题意,需要设置visit数组,如果访问过,则下次则不能访问

老师讲得不详细,这个博客有详细:别人的解释
顺序:在这里插入图片描述

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;

//骑士可以前行的8个方向用二维数组存储.按照字典从小到大
int direction[8][2] = {
        {-1,-2}, {1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2}
};

const int MAXN = 30;

//棋盘参数
int p,q;

//定义visit数组,二维数组
bool visit[MAXN][MAXN];


//深度优先搜索,返回bool值:能否找到一条能遍历完所有棋盘的路径
bool DFS(int x, int y, int step,string ans) {
    if(step == p * q) {                         //搜索成功
        cout << ans << endl << endl;
        return true;
    } else {
        for(int i = 0; i < 8;i++) {             //遍历邻居节点
            int nx = x + direction[i][0];       //扩展状态坐标
            int ny = y + direction[i][1];
            char col = ny + 'A';                //该点编号
            char row = nx + '1';
            if(nx < 0 || nx >= p || ny < 0 || ny >= q || visit[nx][ny]) {
                continue;
            }
            visit[nx][ny] = true;               //标记该点
            if(DFS(nx,ny,step+1,ans+col+row)) {
                return true;
            }
            visit[nx][ny] = false;              //取消标记
        }
    }
    return false;
}

int main(){
    int n;
    scanf("%d", &n);
    int caseNumber = 0;
    while (n--){
        scanf("%d%d",&p,&q);
        memset(visit,false,sizeof(visit));
        cout << "Scenario #" << ++caseNumber << ":" << endl;
        visit[0][0] = true;                     //标记A1点
        if(!DFS(0,0,1,"A1")) {
            cout << "impossible" << endl << endl;
        }
    }
    return 0;
}

阅读算法笔记p268,多读书
计算机常识:
数据结构和操作系统中的栈,堆的区别

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gylagyl97

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值