小白学BFS:迷宫最短路径

问题描述

给定N*N的迷宫(3<=N<=7),有1~M个必经节点(2<=M<=5)。其中1为起点,M为终点。迷宫中除了必经节点1~M,0表示通路,可以经过,9表示障碍物,必须绕行。在寻找下一必经节点的时候,可以经过其他必经节点。要求计算出从1开始按数字大小顺序经过每一个必经节点到达终点的最短路径的个数

输入描述

输入T个迷宫,迷宫的大小为N*N,迷宫中必经节点为1~M。

输入样例

5
5 4
1 0 0 0 0
0 0 0 0 0
0 2 0 4 0
9 0 0 9 0
0 0 0 3 0
3 3 
1 0 0 
0 2 9
0 9 3
6 5
0 0 0 0 0 9
0 0 0 0 9 0
0 0 0 5 0 0
2 9 9 0 0 9
3 0 0 0 0 0
1 0 0 0 0 4
7 2
0 0 0 0 0 0 0
0 9 9 9 9 9 0
0 0 0 0 0 9 0
0 9 9 0 0 9 0
0 9 0 0 2 9 0
0 9 9 9 9 9 0 
0 0 0 0 0 0 1
7 5
1 3 0 0 0 0 0 
9 0 0 0 0 0 0
0 0 5 0 0 0 0 
0 0 0 9 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 4
0 0 0 0 0 0 2

输出样例

#1 18
#2 0
#3 42
#4 3
#5 156508320

算法思路

BFS+队列,每个队列节点包括当前点的坐标(x,y)和当前节点所在的访问层数num和当前节点想找的下一个必经节点next。

设置访问数组visit[7][7]、visit1[7][7]、visit2[7][7]、visit3[7][7]用于记录1->2、2->3、3->4、4->5的访问情况。访问数组初始值都为-1,当第一次访问的时候,记录当前访问层数,如果后续访问层数>已经记录的层数,说明当前一定不是最短路径,直接结束本次循环。

当访问到终点,最短路径标志flag+1。

其他思路见代码。。以后有时间再加。。

代码实现(思路+测试疯狂注释版)

#include <iostream>

using namespace std;

int testcase;
int map[7][7];
int visit[7][7];
int visit1[7][7];
int visit2[7][7];
int visit3[7][7];
int flag;
int Answer = 1;
int N, M;
int answer[6];
int minLength[6];
int dx[4] = { 0, 1, 0, -1 };
int dy[4] = { 1, 0, -1, 0 };
const int X = 9999;

struct record
{
    int x;
    int y;
    int next;
    int num;
};

typedef struct QueNode
{
    record data[X];
    int front;
    int rear;
} *SeQue;

SeQue S;
//SeQue S1;
//SeQue S2;
//SeQue S3;

void InitQue(SeQue S)
{
    S->front = S->rear = 0;
}

bool QueEmpty(SeQue S)
{
    if (S->front == S->rear)
        return true;
    else
        return false;
}

bool QueFull(SeQue S)
{
    if ((S->rear + 1) % X == S->front)
        return true;
    else
        return false;
}

void push(SeQue S, record val)
{
    if (!QueFull(S))
    {
        S->data[S->rear] = val;
        S->rear = (S->rear + 1) % X;
    }
}

void pop(SeQue S)
{
    if (!QueEmpty(S))
        S->front = (S->front + 1) % X;
}

int QueLength(SeQue S)
{
    int length;
    length = (S->rear - S->front + X) % X;
    return length;
}

void Ans()
{
    for (int i = 2; answer[i] > 0, i <= M; i++)
    {
        //cout << "第" << i << "个answer:" << answer[i] << endl;
        Answer *= answer[i];
    }
}

void print()
{
    cout << "#" << testcase << " ";
    if (flag == 0)
    {
        cout << 0 << endl;
    }
    else
    {
        Ans();
        cout << Answer << endl;
    }
}

// BFS(当前层数, 下一个要找的位置)
void BFS()
{
    while (!QueEmpty(S))
    {
        // 一切以队头操作为准   
        //cout << "=========" << endl;
        record pos = S->data[S->front];
        //cout << "当前处理节点位置:(" << pos.x << "," << pos.y << "),  当前处理节点值:" << map[pos.x][pos.y] << "  当前想找的下一个节点值:" << pos.next << "  当前节点层数:" << pos.num << endl;
        //cout << "当前队头位置:" << S->front << "  当前队头的值:(" << S->data[S->front].x << "," << S->data[S->front].y << "),  当前队尾位置:" << S->rear << endl;
        pop(S);
        //cout << "出队后当前队头位置:" << S->front << "  当前队头的值:(" << S->data[S->front].x << "," << S->data[S->front].y << "),  当前队尾位置:" << S->rear << endl;
        //比较层数,如果现在访问的节点是以前已经访问过的且这次访问的层数比之前的高,说明这个是走回去了。
        //cout << "看看访问数组的值?";
        if (pos.next == 2)
        {
            //cout << visit[pos.x][pos.y] << endl;
            if (visit[pos.x][pos.y] == -1)
            {
                //cout << "寻找位置2,该节点是第一次访问,记录节点的访问层数" << endl;
                visit[pos.x][pos.y] = pos.num; // 记录节点的访问层数
            }
            else if ((pos.num > visit[pos.x][pos.y]) || ((pos.num > minLength[pos.next]) && (minLength[pos.next] > 0)))
            {
                //cout << "continue 1" << endl;
                continue;
            }
        }
        if (pos.next == 3)
        {
            //cout << visit1[pos.x][pos.y] << endl;
            if (visit1[pos.x][pos.y] == -1)
            {
                //cout << "寻找位置3,该节点是第一次访问,记录节点的访问层数" << endl;
                visit1[pos.x][pos.y] = pos.num; // 记录节点的访问层数
            }
            else if (pos.num > visit1[pos.x][pos.y] || ((pos.num > minLength[pos.next]) && (minLength[pos.next] > 0)))
            {
                //cout << "continue 1" << endl;
                continue;
            }
        }
        if (pos.next == 4)
        {
            //cout << visit2[pos.x][pos.y] << endl;
            if (visit2[pos.x][pos.y] == -1)
            {
                //cout << "寻找位置4,该节点是第一次访问,记录节点的访问层数" << endl;
                visit2[pos.x][pos.y] = pos.num; // 记录节点的访问层数
            }
            else if (pos.num > visit2[pos.x][pos.y] || ((pos.num > minLength[pos.next]) && (minLength[pos.next] > 0)))
            {
                //cout << "continue 1" << endl;
                continue;
            }
        }
        if (pos.next == 5)
        {
            //cout << visit3[pos.x][pos.y] << endl;
            if (visit3[pos.x][pos.y] == -1)
            {
                //cout << "寻找位置5,该节点是第一次访问,记录节点的访问层数" << endl;
                visit3[pos.x][pos.y] = pos.num; // 记录节点的访问层数
            }
            else if (pos.num > visit3[pos.x][pos.y] || ((pos.num > minLength[pos.next]) && (minLength[pos.next] > 0)))
            {
                //cout << "continue 1" << endl;
                continue;
            }
        }
        // 如果走到终点了
        //出现问题:如果前面节点还没访问完就恰好遇到终点,会提前记录,这不对
        //加上条件:必须此时下一个要找的也是终点
        if ((map[pos.x][pos.y] == M) && (pos.next == M))
        {
            
            //cout << "到终点了" << endl;
            flag++;
            //如果是第一次到这个点,说明是最短路径,记录最短值
            if (answer[M] == 0)
            {
                minLength[M] = pos.num;
                answer[M]++;
            }
            //如果不是第一次到这个点,则比较当前步数和已记录的最小值,如果相等则路径+1
            else if (pos.num == minLength[M])
            {
                answer[M]++;
            }
            else
            {
                //cout << "continue 2" << endl;
                continue;
            }
        }
        //如果位置非法,则跳过
        else if ((pos.x < 0) || (pos.y < 0) || (pos.x >= N) || (pos.y >= N) || map[pos.x][pos.y] == 9)
        {
            //cout << "continue 3" << endl;
            continue;
        }
        //
        else if (map[pos.x][pos.y] == pos.next)
        {
            /*pos.num = 0;
            pos.next += 1;
            push(S1, pos);*/
            //visit:记录1->2的访问记录
            //visit1:记录2->3的访问记录
            //visit2:记录3->4的访问记录
            //visit3:记录4->5的访问记录
            if (pos.next == 2)
            {
                visit1[pos.x][pos.y] = 0;
            }
            if (pos.next == 3)
            {
                visit2[pos.x][pos.y] = 0;
            }
            if (pos.next == 4)
            {
                visit3[pos.x][pos.y] = 0;
            }
            //cout << "找到下一个了" << endl;
            if (answer[pos.next] == 0)
            {
                //cout << "第一次找到下一个,记录最短步数:" << pos.num << endl;
                minLength[pos.next] = pos.num;
                //cout << "看看是否记录了?位置:minLength[" << pos.next << "] = " << minLength[pos.next] << endl;
                answer[pos.next]++;
                //cout << "看看是否记录到答案里了?answer[" << pos.next << "] = " << answer[pos.next] << endl;
                //防止之前没经过必经点已经先访问到经过必经点后出来的点导致访问失败了,且路径是一段一段算的
                //所以这里新插入的点路径长度置为1,这样一定可以避免这个问题
                //第二个问题:这时候再往后走如果又走回去会发生问题,当前找到下一个点的最短路径记录的是上一个找过来的
                //如果继续往后走又走回来会更短,被记录进去
                //如果已经处理找到这个点的下一个点了,前面能走的应该都走完了吧?
                //如果找到这个后把visit值归0应该不会有影响?
                //但是这里是第一次找到,在这里归0不行,不然打个标记?
                //不然从你开始新的一轮BFS?这个好像可以...这样也不好,这得写多少层函数啊?不然DFS+BFS呢、、
                //可以多建几个visit表,根据next来确定访问哪个visit表
                for (int i = 0; i < 4; i++)
                {
                    int new_x = pos.x + dx[i];
                    int new_y = pos.y + dy[i];
                    if ((new_x >= 0) && (new_y >= 0) && (new_x < N) && (new_y < N) && (map[new_x][new_y] != 9))
                    {
                        record newpos{};
                        newpos.x = new_x;
                        newpos.y = new_y;
                        newpos.next = pos.next + 1;
                        newpos.num = 1;
                        //cout << "push(" << newpos.x << "," << newpos.y << ")" << "  push进去的点当前层数:" << newpos.num << endl;
                        push(S, newpos);
                    }
                }
            }
            else if (pos.num == minLength[pos.next])
            {
                answer[pos.next]++;
                //cout << "看看是否记录到答案里了?answer[" << pos.next << "] = " << answer[pos.next] << endl;
                //重复记录了
                /*for (int i = 0; i < 4; i++)
                {
                    int new_x = pos.x + dx[i];
                    int new_y = pos.y + dy[i];
                    if ((new_x >= 0) && (new_y >= 0) && (new_x < N) && (new_y < N) && (map[new_x][new_y] != 9))
                    {
                        record newpos{};
                        newpos.x = new_x;
                        newpos.y = new_y;
                        newpos.next = pos.next + 1;
                        newpos.num = pos.num + 1;
                        cout << "push(" << newpos.x << "," << newpos.y << ")" << endl;
                        push(S, newpos);
                    }
                }*/
            }
            else
            {
                //cout << "continue 4" << endl;
                continue;
            }
        }
        else //if ((map[pos.x][pos.y] == 0) || (map[pos.x][pos.y] != pos.next))
        {
            //cout << "这个点可以走通" << endl;
            for (int i = 0; i < 4; i++)
            {
                int new_x = pos.x + dx[i];
                int new_y = pos.y + dy[i];
                if ((new_x >= 0) && (new_y >= 0) && (new_x < N) && (new_y < N) && (map[new_x][new_y] != 9))
                {
                    record newpos{};
                    newpos.x = new_x;
                    newpos.y = new_y;
                    newpos.next = pos.next;
                    newpos.num = pos.num + 1;
                    //cout << "push(" << newpos.x << "," << newpos.y << ")" << "  push进去的点当前层数:" << newpos.num << endl;
                    push(S, newpos);
                }
            }
        }
    }
}

int main()
{
    S = new QueNode;
    /*S1 = new QueNode;
    S2 = new QueNode;
    S3 = new QueNode;*/
    InitQue(S);
    /*InitQue(S1);
    InitQue(S2);
    InitQue(S3);*/
    for (int i = 0; i < 7; i++)
        for (int j = 0; j < 7; j++)
        {
            visit[i][j] = -1;
            visit1[i][j] = -1;
            visit2[i][j] = -1;
            visit3[i][j] = -1;
        }
    testcase++;
    int T;
    record start{};
    //cout << "Input the number of maps: ";
    cin >> T;
    //cout << "No." << testcase << ": Input the size of the map and the destination point number" << endl;
    for (testcase; testcase <= T; testcase++)
    {
        cin >> N >> M;
        //cout << "Input the map (0 for empty, 1 for start, 9 for obstacle):" << endl;
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                cin >> map[i][j];
                if (map[i][j] == 1)
                {
                    start.x = i;
                    start.y = j;
                    start.next = 2;
                    start.num = 0;
                }
            }
        }
        //cout << "push(" << start.x << "," << start.y << ")" << "  push进去的点当前层数:" << start.num << endl;
        push(S, start);
        /*for (int i = 0; i < 4; i++)
        {
            int new_x = start.x + dx[i];
            int new_y = start.y + dy[i];
            if ((new_x >= 0) && (new_y >= 0) && (new_x < N) && (new_y < N) && (map[new_x][new_y] != 9))
            {
                record newpos{};
                newpos.x = new_x;
                newpos.y = new_y;
                newpos.next = start.next;
                newpos.num = start.num + 1;
                cout << "push(" << newpos.x << "," << newpos.y << ")" << endl;
                push(S, newpos);
            }
        }*/
        BFS();
        print();
        //打印完之后要记得清空用到的中间过程。
        flag = 0;
        Answer = 1;
        for(int i=0;i<7;i++)
            for (int j = 0; j < 7; j++)
            {
                map[i][j] = 0;
                visit[i][j] = -1;
                visit1[i][j] = -1;
                visit2[i][j] = -1;
                visit3[i][j] = -1;
            }
        for (int i = 0; i < 6; i++)
        {
            minLength[i] = 0;
            answer[i] = 0;
        }
    }
    return 0;
}

去掉注释版的就不放了,代码包含小白每一步的思路和遇到的问题,最终完成了这个不难的问题。

运行结果

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用广度优先搜索算法求解迷宫最短路径的 C 语言代码: ```c #include <stdio.h> #include <stdlib.h> #define ROW 10 // 迷宫行数 #define COL 10 // 迷宫列数 // 定义结构体表示坐标 typedef struct { int x; // 行坐标 int y; // 列坐标 } Coordinate; // 定义队列结构体 typedef struct { Coordinate data[ROW * COL]; // 队列数据 int front; // 队首指针 int rear; // 队尾指针 } Queue; // 初始化队列 void initQueue(Queue *q) { q->front = q->rear = 0; } // 判断队列是否为空 int isQueueEmpty(Queue *q) { return q->front == q->rear; } // 入队操作 void enqueue(Queue *q, Coordinate c) { q->data[q->rear++] = c; } // 出队操作 Coordinate dequeue(Queue *q) { return q->data[q->front++]; } // 判断坐标是否越界 int isOutOfBound(Coordinate c) { return c.x < 0 || c.y < 0 || c.x >= ROW || c.y >= COL; } // 判断坐标是否为墙 int isWall(int maze[ROW][COL], Coordinate c) { return maze[c.x][c.y] == 1; } // 判断坐标是否已经访问过 int isVisited(int visited[ROW][COL], Coordinate c) { return visited[c.x][c.y] == 1; } // 标记坐标已经访问过 void markVisited(int visited[ROW][COL], Coordinate c) { visited[c.x][c.y] = 1; } // 判断是否为出口 int isExit(Coordinate c, Coordinate exit) { return c.x == exit.x && c.y == exit.y; } // 广度优先搜索求解最短路径 int bfs(int maze[ROW][COL], Coordinate start, Coordinate exit) { int visited[ROW][COL] = {0}; // 标记已经访问过的坐标 int distance[ROW][COL] = {0}; // 记录起点到每个坐标的距离 Queue q; // 定义队列 initQueue(&q); // 初始化队列 enqueue(&q, start); // 入队起点 markVisited(visited, start); // 标记起点已经访问过 while (!isQueueEmpty(&q)) { Coordinate current = dequeue(&q); // 取出队首元素 if (isExit(current, exit)) { // 到达出口 return distance[current.x][current.y]; // 返回最短路径长度 } // 枚举当前坐标的四个方向 Coordinate dirs[4] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; for (int i = 0; i < 4; i++) { Coordinate next = {current.x + dirs[i].x, current.y + dirs[i].y}; if (isOutOfBound(next) || isWall(maze, next) || isVisited(visited, next)) { continue; // 如果下一个坐标越界、是墙或已经访问过,继续枚举下一个方向 } enqueue(&q, next); // 将下一个坐标入队 markVisited(visited, next); // 标记下一个坐标已经访问过 distance[next.x][next.y] = distance[current.x][current.y] + 1; // 更新下一个坐标的距离 } } return -1; // 没有找到最短路径 } int main() { int maze[ROW][COL] = { {0, 1, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 1, 0, 1, 1, 1, 0, 1, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 0} }; Coordinate start = {0, 0}; // 起点坐标 Coordinate exit = {9, 9}; // 出口坐标 int distance = bfs(maze, start, exit); // 求解最短路径长度 if (distance == -1) { printf("迷宫没有出口!\n"); } else { printf("最短路径长度为:%d\n", distance); } return 0; } ``` 以上代码中,我们使用了一个队列来存储待访问的坐标,使用二维数组 `visited` 记录已经访问过的坐标,使用二维数组 `distance` 记录起点到每个坐标的距离。在广度优先搜索算法中,我们每次取出队首元素,枚举其四个方向,对于每个方向,如果下一个坐标越界、是墙或已经访问过,则继续枚举下一个方向;否则,将下一个坐标入队,标记下一个坐标已经访问过,更新下一个坐标的距离。当取出的元素到达出口时,算法结束,返回最短路径长度。如果队列为空仍然没有找到出口,则说明迷宫没有出口。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值