zoukankan      html  css  js  c++  java
  • 迷宫寻路问题全解

    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;
    }
    View Code

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

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

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

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

    #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;
    }
    View Code

    注意:

    • 当 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 = 0, 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();
            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)) {
                    if (tox == endx && toy == endy) {
                        cout << cnt[head] + 1 << endl;//获得当前的层数
                        return;
                    }
                    //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;
    }
    View Code

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

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

    注意:

    ①:BFS搜到的第一条一定是最短的,但是最短的不一定只有一条。题目未说明有唯一解的,要注意题目对解的要求。

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

    ③:BFS没有办法想DFS那样直接把路径存下来;只能把每个点的前驱记下来,这样最后到了终点,得到的路径正好是是反过来的。两种选择,1、自行处理倒过来输出。2、BFS直接倒过来搜,即从终点向起点搜,负负得正

    #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();
            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)) {
    
                    if (tox == 0 && toy == 0) {
                        cout << "(" << 0 << ", " << 0 << ")" << endl;
                        while (!(x == 4 && y == 4)) {
                            cout << "(" << x << ", " << y << ")" << endl;
                            int from_x = path[x][y][0];
                            int from_y = path[x][y][1];
                            x = from_x; y = from_y;
                        }
                        cout << "(" << 4 << ", " << 4 << ")" << endl;
                        return;
                    }
    
                    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;
    }
    View Code

    优化:双向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;
    }
    View Code

     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;
    }
    View Code
  • 相关阅读:
    Pandas提取数据存入excel
    获取页面元素二
    selenium如何解决IE自动填充表单问题
    IE浏览器相关的问题及解决方案
    TestNG入门教程(TestNG介绍、在Eclipse中安装TESTNG、测试小例子、基本注解、如何执行测试、按顺序执行Case、异常测试、组合测试、参数化测试、忽略测试、依赖测试、测试结果报告)
    selenium Webdriver 处理 —— 通过时间控件给文本框赋值
    列表翻页,选中目标记录
    bug统计
    在eclipse中安装TestNG
    selenium搭建、运行
  • 原文地址:https://www.cnblogs.com/czc1999/p/10600829.html
Copyright © 2011-2022 走看看