BFS最短経路の2つの印刷方法

注意:私が2年間で書いた新しい本「アルゴリズムコンペティション」は、2022年2月に清華大学出版社に引き渡され、2022年7月に出版される予定です。
「アルゴリズムコンペティション」は「大要」であり、「基本-中級-上級」をカバーし、長さは約700ページです。いくつかの知識ポイントのドラフトがこのブログに公開されています。
このブログは、新刊「アルゴリズムコンペティション」の「3.4BFSと最短経路」から抜粋したものです。

1.BFSは最短パスを見つけます

   最短経路問題は最も有名なグラフ理論の問題であり、さまざまなシナリオとアルゴリズムがあります。
   特別なシナリオでは、BFSも優れた最短経路アルゴリズムです。このシナリオでは、隣接する2つのポイントすべての間の距離が等しく、この距離は一般に1と見なされます。現時点では、BFSが最適な最短経路アルゴリズムです。開始点から終了点までの最短距離を見つける計算の複雑さはO(m)です。ここで、mはグラフ上のエッジの数です。これは、各エッジが次のことを行う必要があるためです。一度チェックしてください。
   2点間の距離が等しくない場合、BFSは使用できず、ダイクストラなどの一般的なアルゴリズムを使用する必要があります。
   BFSの特徴は、レイヤーごとの拡散です。つまり、最短経路に従って拡散します。隣接ノードをBFSキューに追加する場合、開始点からの距離の順に追加されます。最初に開始点から1の距離の隣接ノードを追加し、次に2の距離の隣接ノードを追加します。オン。1つのレベルが検索された後、次のレベルの検索が続行されます。パスは開始点から始まり、各レイヤーに沿って徐々に出ていきます。パスの長さは、レイヤーを追加するたびに1ずつ増加します。次に、同じ長さのすべての最短経路が同じレベルから広がります。
   最短経路を見つけるとき、2つの一般的な質問があります:
   (1)最短経路はどれくらいですか?答えは明らかにユニークです;
   (2)最短経路はどのポイントを通過しますか?複数の最短経路が存在する可能性があるため、問題は出力を必要としないことがよくあります。出力が必要な場合、それは通常、辞書式順序が最小の経路です。
   以下は、例を使用して最短経路の計算と最短経路の印刷を紹介します。


2019年州大会ZhentiMazehttps : //www.lanqiao.cn/problems/602/learning/Title
description:次の図は、1でマークされたものが障害物であり、マークされたものが障害物である迷路の平面図を示しています。 0で合格です場所。
010000 000100
001001
110000
迷路
の入口は左上隅、出口は右下隅です。迷路では、1つの位置から、上下左右の4方向のいずれかにしか歩くことができません。上記の迷路は、入り口から、DRRURRDDDRの順で合計10ステップで迷路を通り抜けることができます。その中で、D、U、L、およびRは、それぞれ下、上、左、および右に下がることを表します。以下のより複雑な迷路(30行50列)の場合、最も少ないステップを使用する迷路を通過する方法を見つけ、最も少ないステップで辞書式順序が最小の答えを見つけます。
辞書式順序でD<L<R<Uであることに注意してください。


  この問題は、最短パスの基本的なBFS検索です。BFSは最適なアルゴリズムであり、各ポイントを1回だけ検索する必要があります。つまり、キュー内で1回、キュー内で1回です。
  この質問では、辞書式順序が最小の最短パスを返す必要があります。次のレイヤーが拡散される(次のレイヤーのノードをBFSキューに追加する)たびに、辞書式順序「D<」の順序になります。 L <R <U "ノードの次のレイヤーを追加するには、最初に見つかった最短パスが最小の辞書式順序になります。
  この質問の鍵はパス印刷です。2つの印刷方法を以下に示します。

2.パス印刷の簡単な方法

  簡単な方法で、小さな写真に適しています。
  ポイントvに展開するたびに、開始ポイントsからvへのフルパスパスがvに保存されます。終点tに到達すると、始点sからtまでの完全なパスが取得されます。
  以下のコードでは、原点からこのポイントまでのパスが各ノードで記録されています。エンドポイントに到達したら、cout << now.path<<endl;を使用してフルパスを出力します。
  これの欠点は、フルパスが各ポイントに格納されるため、多くのスペースを占有することです。

#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.パス印刷の標準的な方法

  大きな画像に適した標準的な方法。
  実際、各ノードに完全なパスを保存する必要はありませんが、各ポイントで先行ノードを記録するだけで十分です。これにより、エンドポイントを開始ポイントまで段階的に追跡でき、完全なパスを取得できます。得られます。このパス記録方式を「標準方式」と呼びます。
  コード内のprint_path()に注意してください。これは再帰関数であり、再帰してから出力されます。終点から開始し、始点に戻ってから、始点から終点までの完全なパスを正の順序で印刷します。

#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();
}

おすすめ

転載: blog.csdn.net/weixin_43914593/article/details/123677270