文章

用BFS解决迷宫最短路径问题的一些思考

Some Words

作为一个刚刚步入大二的没几周的大学生,从第二周就被要求去搞定最短路径,解决DFS和BFS算法是吧。

魏XX你坏事做尽!!!

(╹ڡ╹ )但是还是有很多益处的,接下来我会来写写关于这两个算法的内容。

因为我很懒(●ˇ∀ˇ●),所以暂时只更新BFS的

Problem Description

以一个M×N的长方阵表示迷宫。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的最佳通路。** **

实验目的:继续熟练掌握栈的特点;灵活应用栈和队列。

具体要求:

(1)以一个M×N的长方阵表示迷宫,1和0分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的最佳通路,或得出没有通路的结论。(所谓最佳通路是指在所有的通路中输出步长最短的一条通路。)

(2)首先创建一个迷宫,输入格式为:M N,指定迷宫的行数和列数,然后按行输入迷宫的每一行的布局信息(参见输入示例)。求得的通路以三元组(i, j, d)的形式输出(每行输出5组),其中:(i, j)表示迷宫的坐标,d表示走到下一坐标的方向(值为1、2、3、4,分别对应右、下、左、上方向)。迷宫入口坐标(左上角)为(1,1),出口坐标为右下角(M,N),若有通路,则最后输出的坐标三元组格式为(M,N,0)。

(3) 本题目要求可以连续输入多组迷宫数据进行测试,若输入迷宫的行数和列数分别为0,则输入结束。注意输出格式的要求。

(4)提示:用栈和队列都可实现。使用栈从所有可能的通路中寻找最短路径。使用队列可通过广度优先算法直接确定最短路径。

Input

示例输入:

4 5 ———-迷宫的行数和列数

1 0 0 0 0

1 1 1 0 1

1 0 0 1 1

1 1 1 1 1

4 4 ———-迷宫的行数和列数

1 0 0 1

1 0 1 1

1 0 0 1

1 0 1 1

0 0 ———-输入结束标志

Output

若迷宫有通路,则输出迷宫的通路,以每行输出5组的形式控制输出格式;若迷宫没有通路则输出“没有通路”。如上例的输入对应的输出为:

(1,1,2)(2,1,2)(3,1,2)(4,1,1)(4,2,1) (4,3,1)(4,4,1)(4,5,0)

没有通路

Thinking Process about this problem

是的,第一眼看到这个题目完全懵逼。

我是在做竞赛题吗?

然后通过强大的搜索引擎了解到BFS与DFS两个算法,并首先去运用DFS去尝试解决该问题

这不是讲BFS的吗?

但是失败了,也许DFS是最快找到路径的,但是找到的路径未必是最短的。而我便因为这个特性而忽略了最短这个特点。

以下是DFS初期实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <iostream>
#include <vector>
#include <stack>
using namespace std;

struct Point {
    int x;
    int y;
    int direction;
    Point(int _x, int _y, int _direction) : x(_x), y(_y), direction(_direction) {}
};

// 检查坐标是否合法
bool isValid(int x, int y, int m, int n) {
    return x >= 0 && x < m && y >= 0 && y < n;
}

// 检查当前坐标是否为出口
bool isExit(int x, int y, int m, int n) {
    return x == m - 1 && y == n - 1;
}

// 输出路径
void printPath(stack<Point>& path, int m, int n) {
    vector<Point> temp;
    while (!path.empty()) {
        temp.push_back(path.top());
        path.pop();
    }
    int count = 0;
    for (int i = temp.size() - 1; i >= 0; i--) {
        cout << "(" << temp[i].x + 1 << "," << temp[i].y + 1 << "," << temp[i].direction << ")";
        count++;
        if (count == 5) {
            cout << endl;
            count = 0;
        }
        if (i == 0)
        {
            cout << "(" << m << "," << n << "," << 0 << ")" << endl;
        }

    }

}
// 求解迷宫最佳通路
void findPath(vector<vector<int>>& maze, int m, int n) {
    stack<Point> path;
    int visited[100][100];
    for (int i = 0;i < 100;i++)
    {
        for (int j = 0;j < 100;j++) {
            visited[i][j] = 0;
        }
    }
    // 入口坐标
    int startX = 0;
    int startY = 0;

    // 出口坐标
    int endX = m - 1;
    int endY = n - 1;

    // 右、下、左、上四个方向
    int dx[] = { 0, 1, 0, -1 };
    int dy[] = { 1, 0, -1, 0 };

    // 将起点入栈并标记为已访问
    path.push(Point(startX, startY, 0));
    visited[startX][startY] = 1;

    while (!path.empty()) {
        Point curr = path.top();
        path.pop();

        int x = curr.x;
        int y = curr.y;
        int direction = curr.direction;
  
        if (isExit(x, y, m, n)) {
            // 输出路径
            printPath(path, m, n);
            return;
        }

        for (int i = direction; i < 4; i++) {
            int newX = x + dx[i];
            int newY = y + dy[i];

            if (isValid(newX, newY, m, n) && maze[newX][newY] == 1 && visited[newX][newY]==NULL) {
                path.push(Point(x, y, i + 1));
                visited[newX][newY] = 1;
                path.push(Point(newX, newY, 0));
                break;
            }
        }
    }

    // 没有通路
    cout << "没有通路" << endl;
}

int main() {
    int m, n;
    int count = 0;
    while (cin >> m >> n) {
        if (m == 0 && n == 0) {
            break;
        }
         count++;
        vector<vector<int>> maze(m, vector<int>(n));

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                cin >> maze[i][j];
            }
        }

        findPath(maze, m, n);
    }
}

最后的结果就是在oj的答案错误里度过了每一天(恼

所以为什么不尝试BFS呢(喜

BFS Answer

不多说了,直接发代码吧

Origin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <iostream>
#include <queue>
using namespace std;
int m, n;
int maze[100][100];
struct Point {
    int x, y, direction;
    Point(int _x = 0, int _y = 0, int _direction = 0) : x(_x), y(_y), direction(_direction) {}
};
int dy[] = { 1,0,-1,0 };
int dx[] = { 0,1,0,-1 };
void bfs()
{
    bool visited[100][100];
    for (int i = 0;i < 100;i++)
    {
        for (int j = 0;j < 100;j++) {
            visited[i][j] = false;
        }
    }
    struct Point pre[100][100];
    queue<Point> q;
    int s_x = 1, s_y = 1;
    pre[s_x][s_y].x = -1;
    pre[s_x][s_y].y = -1;
    pre[s_x][s_y].direction = 0;
    q.push(Point(1, 1, 0));
    visited[s_x][s_y] = true;
    while (q.size()) {
        Point k = q.front();
        q.pop();
        for (int i = k.direction;i < 4;i++) {
            int x = k.x + dx[i];
            int y = k.y + dy[i];
            if (x >= 1 && x <= m && y >= 1 && y <= n && maze[x][y] == 1 && !visited[x][y]) {
                visited[x][y] = true;
                q.push(Point(x, y, 0));
                pre[x][y].x = k.x;
                pre[x][y].y = k.y;
                pre[x][y].direction = i + 1;
            }
 
        }
    }
    int x = m, y = n, d = 0;
    Point p[100];
    if (pre[x][y].x == 0 && pre[x][y].y == 0)
    {
         
        cout << "没有通路" ;
        return;
    }
    else
    {
        int i = 0;
        while (x != -1 && y != -1) {
            p[i].x = x;p[i].y = y;p[i].direction = d;
            Point p = pre[x][y];
            x = p.x;
            y = p.y;
            d = p.direction;
            i++;
        }
        int count = 0;
        for (int j = i-1;j >= 0;j--)
        {
            cout << "(" << p[j].x << "," << p[j].y << "," << p[j].direction << ")";
            count++;
            if (count == 5) {
                cout << endl;
                count = 0;
            }
        }
 
    }
 
 
 
}
int main() {
 
    while (cin >> m >> n) {
        if (m == 0 && n == 0) {
            return 0;
        }
 
 
        for (int i = 1;i <= m;i++) {
            for (int j = 1;j <= n;j++) {
                cin >> maze[i][j];
            }
        }
        bfs();
        cout << endl;
    }
}

我觉得算法本身没有什么特别的,最重要的便是如何读取你走过路径的结点

而本代码便提供了一种很精妙,类似指针一样的东西,仿佛将每个节点的信息联系到了一起。

这感觉很不错!

最后再附上面向对象版吧!

Object oriented

数组懒得初始化,自己去封装到类里(╹ڡ╹ )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <iostream>
#include <queue>
int maze[100][100];
using namespace std;
int dy[] = { 1,0,-1,0 };
int dx[] = { 0,1,0,-1 };
class Point {
    int x, y, direction;
public:
    Point(int _x = 0, int _y = 0, int _direction = 0) : x(_x), y(_y), direction(_direction) {}
    friend class Find_Path;
 
 
};
class Find_Path {
 
    int m,  n;
public:
    Find_Path(int m, int n) {
        this->m = m;
        this->n = n;
         
    }
    void bfs() {
        bool visited[100][100];
        for (int i = 0;i < 100;i++)
        {
            for (int j = 0;j < 100;j++) {
                visited[i][j] = false;
            }
        }
        struct Point pre[100][100];
        queue<Point> q;
        int s_x = 1, s_y = 1;
        pre[s_x][s_y].x = -1;
        pre[s_x][s_y].y = -1;
        pre[s_x][s_y].direction = 0;
        q.push(Point(1, 1, 0));
        visited[s_x][s_y] = true;
        while (q.size()) {
            Point k = q.front();
            q.pop();
            for (int i = k.direction;i < 4;i++) {
                int x = k.x + dx[i];
                int y = k.y + dy[i];
                if (x >= 1 && x <= m && y >= 1 && y <= n && maze[x][y] == 1 && !visited[x][y]) {
                    visited[x][y] = true;
                    q.push(Point(x, y, 0));
                    pre[x][y].x = k.x;
                    pre[x][y].y = k.y;
                    pre[x][y].direction = i + 1;
                }
 
            }
        }
        int x = m, y = n, d = 0;
        Point p[100];
        if (pre[x][y].x == 0 && pre[x][y].y == 0)
        {
 
            cout << "没有通路";
            return;
        }
        else
        {
            int i = 0;
            while (x != -1 && y != -1) {
                p[i].x = x;p[i].y = y;p[i].direction = d;
                Point p = pre[x][y];
                x = p.x;
                y = p.y;
                d = p.direction;
                i++;
            }
            int count = 0;
            for (int j = i - 1;j >= 0;j--)
            {
                cout << "(" << p[j].x << "," << p[j].y << "," << p[j].direction << ")";
                count++;
                if (count == 5) {
                    cout << endl;
                    count = 0;
                }
            }
 
        }
 
 
 
 
    }
};
 
int main() {
    int m, n;
    Find_Path *a;
    while (cin >> m >> n) {
        if (m == 0 && n == 0) {
            return 0;
        }
 
 
        for (int i = 1;i <= m;i++) {
            for (int j = 1;j <= n;j++) {
                cin >> maze[i][j];
            }
        }
        a = new Find_Path(m, n);
        a->bfs();
 
        cout << endl;
    }
}

Final

什么?还有最后?

当然是快马加鞭写出DFS在查找最短路径上的用法啦(懒

/(ㄒoㄒ)/~~要累死了

本文由作者按照 CC BY 4.0 进行授权