迷宫寻路问题全解

1、深度优先搜索(DFS)+回溯

最基本的板子:

void DFS(int x,int y)
{
    if (x,y都与目标点相同)
    {
        得到一个解;
    }
    else
    {
        for (int i = 1; i <= 四个方向; i++)
            if (满足进一步搜索条件)
            {
                为进一步搜索所需要的状态打上标记;
                DFS(to_x, to_y);
                恢复到打标记前的状态;//也就是回溯一步
            }
    }
}

适用类型①:求可行解数量

https://www.luogu.org/problemnew/show/P1605

#include <iostream>
using namespace std;

//上下左右
int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };

int m[10][10];

int N, M, T, cnt;
int SX, SY, EX, EY;

void DFS(int x, int y) {
    if (x < 1 || x > N || y < 1 || y > M) return;
    if (x == EX && y == EY) {
        cnt++;
        return;
    }
    for (int i = 0; i < 4; i++) {
        if (m[x + direction[i][0]][y + direction[i][1]] != 1) {
            m[x + direction[i][0]][y + direction[i][1]] = 1;
            DFS(x + direction[i][0], y + direction[i][1]);
            m[x + direction[i][0]][y + direction[i][1]] = 0;
        }
    }
}
int main() {
    cin >> N >> M >> T;
    cin >> SX >> SY >> EX >> EY;
    for (int i = 0; i < T; i++) {
        int x, y;
        cin >> x >> y;
        m[x][y] = 1;
    }
    m[SX][SY] = 1;
    DFS(SX, SY);
    cout << cnt << endl;
    return 0;
}

适用类型②:输出所有可行解

例题:https://www.luogu.org/problemnew/show/P1238

这类题目需要注意的是,要知道搜索前进方向的顺序,比如本题是:上左右下。

如果题目够严谨的话,一定会写出来的,但如果没写的话,只能根据题目样例去判断。

没法像DFS一样记录下路径,只能记录每个节点的前驱节点,从而形成路径。但是到终点时,只能输出反过来的路径。所以,我们选择从终点走到起点,就能得出正确的路径了。

#include <iostream>
using namespace std;

//上左右下
int direction[4][2] = { {-1,0},{0,-1},{0,1},{1,0} };

int m[20][20];
int path[250][2];

int N, M, T, cnt;
int SX, SY, EX, EY;

void DFS(int x, int y,int k) {
    if (x < 1 || x > M || y < 1 || y > N) return;
    if (x == EX && y == EY) {
        cnt++;
        for (int i = 0; i < k; i++) {
            cout << "(" << path[i][0] << "," << path[i][1] << ")";
            if (i != k - 1)cout << "->";
        }
        cout << endl;
        return;
    }

    for (int i = 0; i < 4; i++) {
        int tox = x + direction[i][0], toy = y + direction[i][1];
        if (m[tox][toy] == 1) {
            m[tox][toy] = 0;
            path[k][0] = tox;
            path[k][1] = toy;
            DFS(tox, toy, k + 1);
            m[tox][toy] = 1;
        }
    }
}
int main() {
    cin >> M >> N;
    for (int i = 1; i <= M; i++) {
        for (int j = 1; j <= N; j++) {
            cin >> m[i][j];
        }
    }
    cin >> SX >> SY >> EX >> EY;
    m[SX][SY] = 0;
    path[0][0] = SX, path[0][1] = SY;
    DFS(SX, SY, 1);
    if (cnt == 0)cout << "-1" << endl;
    return 0;
}

注意:

  • 当 m , n 较大时,无法胜任,撑死在15左右就嗝屁了(还得是迷宫中障碍的位置比较配合的情况,一般大于10,就要慎重考虑该不该用DFS了)。
  • 搜索前进方向的顺序是可能会影响到效率的,如果起点在左上部分,终点在右下部分,理想情况下,优先选择右方向和下方向,效率会更高。
  • 找到的第一条解,不一定是最短的,应该说一般都不是。

2、广度优先搜索(BFS)

适用类型①:最短路径的长度

题目链接:走迷宫

#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;

#define PAIR make_pair
//上下左右
int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };

char m[20][20];
int cnt[250];
int head, tail = 1;
int startx = 0, starty = 1, endx = 9, endy = 8;

void BFS(int x, int y) {
    queue<pair<int, int>>q;
    q.push(PAIR(x, y));
    while (!q.empty()) {
        x = q.front().first; y = q.front().second;
        q.pop();
        if (x == endx && y == endy) {
            cout << cnt[head] << endl;
            return;
        }
        for (int i = 0; i < 4; i++) {
            int tox = x + direction[i][0], toy = y + direction[i][1];
            if (m[tox][toy] == ‘.‘ &&(tox >= startx && tox <= endx && toy >= starty && toy <= endy)) {
                //cnt[head]记录当前到了第几层BFS
                cnt[tail++] = cnt[head] + 1;
                m[tox][toy] = ‘#‘;
                q.push(PAIR(tox, toy));
            }
        }
        head++;
    }
}
int main() {
    while (true) {
        head = 0, tail = 1;
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                if (scanf("%c", &m[i][j]) == EOF) return 0;
            }
            getchar();
        }
        m[startx][starty] = ‘#‘;
        BFS(startx, starty);
    }
    return 0;
}

适用类型②:找到最短的一条路径

题目链接:http://poj.org/problem?id=3984

注意:

BFS搜到的第一条一定是最短的,但是最短的不一定只有一条。题目说明有唯一解的,不用考虑太多。

如果不止一个解,一般题目会给定按照字典序(上下左右用U D L R表示)、优先向某个方向等要求,输出指定解。

#include <iostream>
#include <queue>
using namespace std;

#define PAIR make_pair
//上下左右
int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };

int m[20][20];
int path[15][15][2];

void BFS(int x, int y) {
    queue<pair<int, int>>q;
    q.push(PAIR(x, y));
    while (!q.empty()) {
        x = q.front().first; y = q.front().second;
        q.pop();
        if (x == 0 && y == 0) {
            while (true) {
                cout << "(" << x << ", " << y << ")" << endl;
                if (x == 4 && y == 4)break;
                int from_x = path[x][y][0];
                int from_y = path[x][y][1];
                x = from_x; y = from_y;
            }
            return;
        }
        for (int i = 0; i < 4; i++) {
            int tox = x + direction[i][0], toy = y + direction[i][1];
            if (m[tox][toy] == 0 &&(tox >= 0 && tox <= 4 && toy >= 0 && toy <= 4)) {
                m[tox][toy] = 1;
                path[tox][toy][0] = x;
                path[tox][toy][1] = y;
                q.push(PAIR(tox, toy));
            }
        }
    }
}
int main() {
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            cin >> m[i][j];
        }
    }
    m[4][4] = 0;
    BFS(4, 4);
    return 0;
}

优化:双向BFS

正向BFS,与反向BFS用不同的值取标记地图,第一次相遇时(某一方发现对方的标记值),一定是一条最短的路径。这个时候,path中记录着两段方向相反的路径,输出的时候需要处理。

同样的,如果题目要求是按照某种顺序、优先某个方向;那么反向的BFS只要反着来就行了。

#include <iostream>
#include <stack>
#include <queue>
using namespace std;

#define PAIR make_pair

int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };

int m[20][20];
int path[15][15][2];
bool flag = false;
int SX = 0, SY = 0, EX = 4, EY = 4;
void move_one_step(queue<pair<int,int>>&q,int sign) {
    int x = q.front().first, y = q.front().second; q.pop();
    for (int i = 0; i < 4; i++) {
        int tox = x + direction[i][0], toy = y + direction[i][1];
        if (m[tox][toy] == 0 && (tox >= SX && tox <= EX && toy >= SX && toy <= EY)) {
            m[tox][toy] = sign;
            path[tox][toy][0] = x;
            path[tox][toy][1] = y;
            q.push(PAIR(tox, toy));
        }
        //发现对方标记值
        else if (m[tox][toy] == -sign) {
            int tempx = tox, tempy = toy;
            /*输出 起点 到相遇点*/
            stack<pair<int, int>>s;
            while (!(tox == 0 && toy == 0)) {
                int t1 = tempx, t2 = tempy;
                s.push(PAIR(tempx, tempy));
                tempx = path[t1][t2][0];
                tempy = path[t1][t2][1];
            }
            while (!s.empty()) {
                int xx = s.top().first, yy = s.top().second;
                s.pop();
                cout << "(" << xx << ", " << yy << ")" << endl;
            }
            /*----------------*/
            /*从相遇点到终点*/
            tempx = x, tempy = y;
            while (!(tempx == 4 && tempy == 4)) {
                int t1 = tempx, t2 = tempy;
                cout << "(" << tempx << ", " << tempy << ")" << endl;;
                tempx = path[t1][t2][0];
                tempy = path[t1][t2][1];
            }
            flag = true;
            return;
        }
    }
}

void BFS(queue<pair<int, int>>&f, queue<pair<int, int>>&r) {
    int i = 1;
    while ((!f.empty() || !r.empty()) && !flag) {
        if (i & 1)
            move_one_step(f, 2);
        else
            move_one_step(r, -2);
        i++;
    }
}
int main() {
    queue<pair<int, int>>f, r;

    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            cin >> m[i][j];
        }
    }
    f.push(PAIR(SX, SY));
    r.push(PAIR(EX, EY));
    m[EX][EY] = -2;
    m[SX][SY] = 2;
    cout << "(" << SX << ", " << SY << ")" << endl;
    BFS(f, r);
    cout << "(" << EX << ", " << EY << ")" << endl;
    return 0;
}

3、A*搜索

A*找到的第一个解不一定是最短的,所以A*不能用来去找最短路径(在有多解的情况下);

A*的搜索特性限制,如果用来输出所有可行解,就没有使用的意义了。

所以其实迷宫题并不适合用A*解,除非题目要求比较特殊(只有一条路),仅做参考。

采取

适用类型:找唯一的路径

#include <iostream>
#include <queue>
using namespace std;

#define PAIR make_pair
//上下左右
int direction[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };
int sx = 0, sy = 0, ex = 4, ey = 4;
class Node {
public:
    int x, y, g, h;
    Node(int x, int y,int g){
        this->x = x;
        this->y = y;
        this->g = g;
        h = abs(ex - x) + abs(ey - y);
    }
    bool operator<(Node n)const {
        return n.g + n.h < g + h;
    }
};

int m[20][20];
int path[15][15][2];
int head = 0, tail = 1;
int cnt[255];

void BFS(int x, int y) {
    priority_queue<Node>q;
    //queue<pair<int, int>>q;
    q.push(Node(x, y, cnt[head]));

    while (!q.empty()) {
        x = q.top().x; y = q.top().y;
        q.pop();
        if (x == 0 && y == 0) {
            while (true) {
                cout << "(" << x << ", " << y << ")" << endl;
                if (x == 4 && y == 4)break;
                int from_x = path[x][y][0];
                int from_y = path[x][y][1];
                x = from_x; y = from_y;
            }
            return;
        }
        for (int i = 0; i < 4; i++) {
            int tox = x + direction[i][0], toy = y + direction[i][1];
            if (m[tox][toy] == 0 && (tox >= 0 && tox <= 4 && toy >= 0 && toy <= 4)) {
                cnt[tail++] = cnt[head] + 1;
                m[tox][toy] = 1;
                path[tox][toy][0] = x;
                path[tox][toy][1] = y;
                q.push(Node(tox, toy, cnt[head] + 1));
            }
        }
        head++;
    }
}
int main() {
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            cin >> m[i][j];
        }
    }
    m[4][4] = 0;
    BFS(4, 4);
    return 0;
}

原文地址:https://www.cnblogs.com/czc1999/p/10600829.html

时间: 2024-10-14 16:29:55

迷宫寻路问题全解的相关文章

phpEXCEL操作全解

phpExcel中文帮助手册,列举了各种属性,以及常用的操作方法,难得是每一个都用实例加以说明,希望对大家有所帮助. phpExcel中文帮助手册,不可多得的好文章,供大家学习参考. 1.设置excel的属性: 创建人 $objPHPExcel->getProperties()->setCreator("Maarten Balliauw"); 最后修改人 $objPHPExcel->getProperties()->setLastModifiedBy("

UITextField 全解

IOS-UITextField-全解 //初始化textfield并设置位置及大小 UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20, 20, 130, 30)]; //设置边框样式,只有设置了才会显示边框样式 text.borderStyle = UITextBorderStyleRoundedRect; typedef enum { UITextBorderStyleNone, UITextBorderSt

RAM和yum功能全解

一RPM程序包管理器功能全解 1.RPM简介 RPM全称为"RedHat Package Manager",是有RedHat公司开发的程序包管理器.RPM 是以一种数据库记录的方式将所需要的套件安装到Linux主机的一套程序包管理器. RPM的命名格式: Name-version-release.arch.rpm 套件名称  套件的版本 发行号 适合的硬件平台 扩展名 RPM的组成清单: 1文件清单 2安装或卸载时运行的脚本 3数据库(公共) 4程序包的名称和版本 5依赖关系 6功能说

PHP漏洞全解(三)-客户端脚本植入

本文主要介绍针对PHP网站的客户端脚本植入攻击方式.所谓客户端脚本植入攻击,是指将可以执行的脚本插入到表单.图片.动画或超链接文字等对象内.当用户打开这些对象后,黑客所植入的脚本就会被执行,进而开始攻击. 客户端脚本植入(Script Insertion),是指将可以执行的脚本插入到表单.图片.动画或超链接文字等对象内.当用户打开这些对象后,攻击者所植入的脚本就会被执行,进而开始攻击. 可以被用作脚本植入的HTML标签一般包括以下几种: 1.<script>标签标记的javascript和vb

PHP漏洞全解(二)-命令注入攻击

本文主要介绍针对PHP网站常见的攻击方式中的命令攻击.Command Injection,即命令注入攻击,是指这样一种攻击手段,黑客通过把HTML代码输入一个输入机制(例如缺乏有效验证限制的表格域)来改变网页的动态 生成的内容.使用系统命令是一项危险的操作,尤其在你试图使用远程数据来构造要执行的命令时更是如此.如果使用了被污染数据,命令注入漏洞就产生了. 命令注入攻击 PHP中可以使用下列5个函数来执行外部的应用程序或函数 system.exec.passthru.shell_exec.“(与s

PHP漏洞全解(四)-xss跨站脚本攻击

本文主要介绍针对PHP网站的xss跨站脚本攻击.跨站脚本攻击是通过在网页中加入恶意代码,当访问者浏览网页时恶意代码会被执行或者通过给管理员发信息 的方式诱使管理员浏览,从而获得管理员权限,控制整个网站.攻击者利用跨站请求伪造能够轻松地强迫用户的浏览器发出非故意的HTTP请求,如诈骗性的电汇 请求.修改口令和下载非法的内容等请求. XSS(Cross Site Scripting),意为跨网站脚本攻击,为了和样式表css(Cascading Style Sheet)区别,缩写为XSS 跨站脚本主要

PHP漏洞全解(七)-Session劫持

本文主要介绍针对PHP网站Session劫持.session劫持是一种比较复杂的攻击方法.大部分互联网上的电脑多存在被攻击的危险.这是一种劫持tcp协议的方法,所以几乎所有的局域网,都存在被劫持可能. 服务端和客户端之间是通过session(会话)来连接沟通.当客户端的浏览器连接到服务器后,服务器就会建立一个该用户的session.每个用 户的session都是独立的,并且由服务器来维护.每个用户的session是由一个独特的字符串来识别,成为session id.用户发出请求时,所发送的htt

Fortify SCA 分析代码漏洞全解

上次介绍了用FindBugs辅助分析代码漏洞,这次换了一个工具:Fortify SCA Demo 4.0.0.Fortify是一个在安全方面挺出名的公司,这里就不多说了.先介绍一下主角:Fortify SCA Demo 4.0.0,虽然现在不知道Fortify SCA的版本是多少,但可以肯定的是,Fortify SCA Demo 4.0.0是一个比较旧的Fortify SCA分析器了,并且还是Demo版的,所以无论是界面还是功能上都是比较简陋的.由于Fortify SCA不是开源的工具,这里就不

rpm包管理功能全解

rpm包管理功能全解            linux rpm问题:怎样查看rpm安装包的安装路径                     rpm -qa l grep  xxxxxx.rpm                    rpm的命令:rpm  [OPTIONS]  [PACKAGE_FILE]            安装:-i, --install            升级:-U, --update, -F, --freshen            卸载:-e, --erase