Two printing methods of BFS shortest path

Notice: The new book " Algorithm Competition " I wrote in two years has been handed over to Tsinghua University Press in February 2022 and is expected to be published in July 2022.
"Algorithm Competition" is a " compendium ", covering "basic - intermediate - advanced", about 700 pages in length. Drafts of some knowledge points have been published on this blog.
This blog is excerpted from "3.4 BFS and Shortest Path" in the new book "Algorithm Competition".

1. BFS finds the shortest path

   The shortest path problem is the most famous graph theory problem, with many different scenarios and algorithms.
   In a special scenario, BFS is also an excellent shortest path algorithm. This scenario is that the distance between all two adjacent points is equal, and this distance is generally regarded as 1. At this time, BFS is the optimal shortest path algorithm. The computational complexity of finding the shortest distance from the starting point to the ending point is O(m), where m is the number of edges on the graph, because each edge needs to be checked once.
   If the distance between the two points is not equal, BFS cannot be used, and a general algorithm such as Dijkstra needs to be used.
   The characteristic of BFS is layer-by-layer diffusion, that is, it diffuses out according to the shortest path. When adding neighbor nodes to the BFS queue, they are added in the order of distance from the starting point: first add neighbor nodes with a distance of 1 from the starting point, and then add a neighbor node with a distance of 2, and so on. After one level is searched, it will continue to search for the next level. A path starts from the starting point and goes out gradually along each layer. The length of the path increases by 1 for each additional layer. Then, all shortest paths of the same length spread out from the same level.
   When finding the shortest path, there are two common questions:
   (1) How long is the shortest path? The answer is obviously unique;
   (2) What points does the shortest path pass through? Since there may be more than one shortest path, the problem often does not require output. If output is required, it is generally the path with the smallest lexicographical order .
   The following uses an example to introduce the calculation of the shortest path and the printing of the shortest path.


2019 Provincial Competition Zhenti Maze https://www.lanqiao.cn/problems/602/learning/Title
description: The following figure shows the floor plan of a maze, in which the one marked with 1 is an obstacle, and the one marked with 0 is passable The place.
010000
000100
001001
110000
The entrance of the maze is the upper left corner and the exit is the lower right corner. In the maze, you can only walk from one position to one of the four directions of up, down, left and right. For the above maze, starting from the entrance, you can pass through the maze in the order of DRRURRDDDR, a total of 10 steps. Among them, D, U, L, and R represent going down, up, left, and right, respectively. For the more complex maze below (30 rows and 50 columns), find a way to go through the maze that uses the fewest steps, and find the answer with the smallest lexicographical order given the fewest steps.
Note that D<L<R<U in the lexicographical order.


  This problem is a basic BFS search for the shortest path. BFS is the optimal algorithm, each point only needs to be searched once, that is, once in the queue and once in the queue.
  The question requires to return the shortest path with the smallest lexicographical order, then as long as each time the next layer is diffused (adding the node of the next layer to the BFS queue), it is in the order of lexicographical order "D<L<R<U" To add the next layer of nodes, then the first shortest path found is the smallest lexicographical order.
  The key to this question is path printing. Two printing methods are given below.

2. Simple method of path printing

  Simple method, suitable for small pictures.
  Every time it expands to a point v, the full path path from the starting point s to v is stored on v. When the end point t is reached, the complete path from the start point s to t is obtained.
  In the code below, the path from the origin to this point is recorded at each node. After reaching the end point, use cout<<now.path<<endl; to print out the full path.
  The downside of this is that it takes up a lot of space because the full path is stored at each point.

#include<bits/stdc++.h>
using namespace std;
struct node{
    
    
    int x;
    int y;
//(1)简单方法:
    string path;    //path,记录从起点(0,0)到这个点(x,y)的完整路径
};
char mp[31][51];     //用矩阵存地图
char k[4]={
    
    'D','L','R','U'};  //字典序
int dir[4][2]={
    
    {
    
    1,0},{
    
    0,-1},{
    
    0,1},{
    
    -1,0}};   //4个方向
int vis[30][50];        //标记。vis=1: 已经搜过,不用再搜

void bfs(){
    
    
    node start; 
    start.x=0;  
    start.y=0;
//(1)简单方法:
    start.path="";
    vis[0][0]=1;               //标记起点被搜过
    queue<node>q;
    q.push(start);             //把第一个点放进队列,开始BFS
    while(!q.empty()){
    
    
        node now = q.front();  //取出队首
        q.pop();
        if(now.x==29 && now.y==49){
    
     //第一次达到终点,这就是字典序最小的最短路径
//(1)简单方法:打印完整路径
            cout << now.path << endl;
            return;
        }
        for(int i=0;i<4;i++){
    
      //BFS:扩散邻居结点
            node next;
            next.x = now.x + dir[i][0];  next.y = now.y + dir[i][1];
            if(next.x<0||next.x>=30||next.y<0||next.y>=50)  //越界了
                continue;
            if(vis[next.x][next.y]==1 || mp[next.x][next.y]=='1')
                continue;           //vis=1:已经搜过;  mp=1:是障碍
            vis[next.x][next.y]=1;  //标记被搜过
//(1)简单方法:记录完整路径:复制上一个点的路径,加上这一步
            next.path = now.path + k[i];
            q.push(next);
        }
    }
}
int main(){
    
    
    for(int i=0;i<30;i++)  
    	cin >> mp[i];  //读题目给的地图数据
    bfs();
}

3. Standard method of path printing

  Standard method, suitable for large images.
  In fact, it is not necessary to store the complete path at each node, but it is enough to record its predecessor nodes at each point, so that the end point can be traced back to the start point step by step, and a complete path can be obtained. This path recording method is called the "standard method".
  Pay attention to print_path() in the code, it is a recursive function, it is recursive and then printed. Start from the end point, backtrack to the start point, and then print out the complete path in positive order from the start point to the end point.

#include<bits/stdc++.h>
using namespace std;
struct node{
    
    
    int x;
    int y;
};
char mp[31][51];  //存地图
char k[4]={
    
    'D','L','R','U'};  //字典序
int dir[4][2]={
    
    {
    
    1,0},{
    
    0,-1},{
    
    0,1},{
    
    -1,0}};
int vis[30][50];  //标记。vis=1: 已经搜过,不用再搜

//(2)标准方法:
char pre[31][51];        //   用于查找前驱点。例如pre[x][y] = ‘D’,表示上一个点
                         //往下走一步到了(x,y),那么上一个点是(x-1,y)
void print_path(int x,int y){
    
          //打印路径:从(0,0)到(29,49)
    if(x==0 && y==0)    return;      //回溯到了起点,递归结束,返回
    if(pre[x][y]=='D')  print_path(x-1,y);   //回溯,往上 U
    if(pre[x][y]=='L')  print_path(x,  y+1); //回溯,往右 R
    if(pre[x][y]=='R')  print_path(x,  y-1);
    if(pre[x][y]=='U')  print_path(x+1,y);
    printf("%c",pre[x][y]);                  //最后打印的是终点
}
void bfs(){
    
    
    node start; 
    start.x=0;  
    start.y=0;
    vis[0][0]=1;               //标记起点被搜过
    queue<node>q;
    q.push(start);             //把第一个点放进队列,开始BFS
    while(!q.empty()){
    
    
        node now = q.front();  //取出队首
        q.pop();
        if(now.x==29 && now.y==49){
    
     //第一次达到终点,这就是字典序最小的最短路径
//(2)标准方法:打印完整路径,从终点回溯到起点,打印出来是从起点到终点的正序
            print_path(29,49);
            return;
        }
        for(int i=0;i<4;i++){
    
      //扩散邻居结点
            node next;
            next.x = now.x + dir[i][0];  next.y = now.y + dir[i][1];
            if(next.x<0||next.x>=30||next.y<0||next.y>=50)  //越界了
                continue;
            if(vis[next.x][next.y]==1 || mp[next.x][next.y]=='1')
                continue;           //vis=1:已经搜过;  mp=1:是障碍
            vis[next.x][next.y]=1;  //标记被搜过
//(2)标准方法:记录点(x,y)的前驱
            pre[next.x][next.y] = k[i];
            q.push(next);
        }
    }
}
int main(){
    
    
    for(int i=0;i<30;i++)  
    	cin >> mp[i];  //读题目给的地图数据
    bfs();
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324174462&siteId=291194637