目次
1. 幅優先探索 (BFS) とは?
百度百科事典は次のように述べています。
幅優先探索アルゴリズム(幅優先探索とも呼ばれます) は、最も簡単なグラフ探索アルゴリズムの 1 つであり、このアルゴリズムは多くの重要なグラフ アルゴリズムのプロトタイプでもあります。Dijkstra の単一ソース最短パスアルゴリズムと Prim の最小スパニング ツリーアルゴリズムはどちらも、幅優先探索に似たアイデアを使用しています。そのエイリアスは BFS とも呼ばれ、ブラインド検索法に属します。その目的は、グラフ内のすべてのノードを体系的に展開してチェックし、結果を見つけることです。つまり、結果がどこにあるかに関係なく、結果が見つかるまでグラフ全体を徹底的に検索します。
あまりにも理論的ですが、コアは次のように述べています。
これは、結果を見つけるためにグラフ内のすべてのノードを体系的に展開して調べることを目的とするブラインド検索方法です。結果の可能な場所に関係なく、結果が見つかるまでグラフ全体が徹底的に検索されます。
次のようにも言われました。
ルートノードの位置からトラバースを開始し、トラバース後、ルートノードに隣接する点を順番にトラバースし、隣接点をトラバースした後、前のラウンドで最初にトラバースしたノードの隣接する未トラバース点を引き続きトラバースします。 、すべてのポイントが通過して終了するまで。
一般知識/基本アルゴリズム/幅優先探索.md jimmyflower6/中山第1回中学校情報コンテスト - Gitee.com
これはよく言われますが、それでも少し鈍いです。
簡単に言えば、次のとおりです。
幅優先探索は、グラフを探索するためのアルゴリズムです。最初にある頂点(始点)にいると仮定すると、この時点ではグラフの全体構造はわかりません。目的は、始点から特定の頂点に到達するまでエッジに沿って検索することです。 (つまり終点)。この処理では、頂点に到達するたびに一度、終点かどうかを判断します。幅優先探索は、始点に最も近い頂点から最初に探索します。
以下では、この方法を段階的な図の形で紹介します。
2.グラフィックワイドサーチ(BFS)
Aが始点、Gが終点です。最初は出発点 A にいて、この時点で G がどこにあるのかわかりません。
A から直接到達できる 3 つの頂点 B、C、D が次のステップの候補頂点として設定されます。
候補頂点から頂点を選択します。最初に候補となった頂点が優先され、複数の頂点が同時に候補になった場合は、そのうちの 1 つを任意に選択できます。(最古の候補となる頂点を優先、後述する「先入れ先出し方式」)
ここでは、B、C、D が同時に候補となるため、一番左の頂点 B をランダムに選択します。
選択した頂点 B に移動します。この時点で B にいるので、B が赤くなり、既に検索された頂点がオレンジ色になります。
ここで、候補頂点はFIFO(First-in-first-out)方式で管理されるため、データ構造「queue」を使用できます。(この構造は後で説明します。下にスクロールしてください)
B から直接アクセスできる 2 つの頂点 E と F が候補頂点として設定されます。
このとき、C と D が最古の候補頂点であり、左側の頂点 C を選択します。
選択した頂点 C に移動します。
Cから直接アクセスできる頂点Hを候補頂点とする。
終点に到達するか、すべての頂点を通過するまで、上記の操作を繰り返します。
この例の検索順序は、A、B、C、D、E、F、H、I、J、K です。
A から I までの検索が終了し、頂点 J に到達しました。
終点Gに到達すると探索が終了する。
補足: 以上のことから、幅優先探索の特徴は、開始点から始めて、近くから遠くまで広範囲に探索することであることがわかります。したがって、対象の頂点が開始点に近いほど、検索は早く終了します。
3. 比較と発見
DFS と BFS の違い:
bfs はノードを先入れ先出しでトラバースし、dfs はノードを先入れ先出しでトラバースします。bfs
は階層的に訪問し、dfs はパスに従って最後まで訪問します。現在のノードには未訪問の隣接ノードがないため、最後のノードに戻り、ターゲット ノードが訪問されるか、すべてのノードが訪問されるまで試行を続けます。bfs は、ソース ポイントとターゲット ノードの間の最短距離を見つける
のに適しています。たとえば、最短パスを見つけることができます。dfs は、マッチング スキームの1 つを解決したり、すべての状況をトラバースしたりするのに適しています。たとえば、完全な配置、トポロジカル ソート、特定のポイントへのパスの検索などです。
しかし、幅優先探索 (BFS) の先入れ先出し法を実装するにはどうすればよいでしょうか? ここでは、データ構造 - キューを使用します。
四。ツール - キュー
Queue is a special linear table , which is special in that it only allows Deletes at Front End (front), and insert operations at back end of the table. スタックと同様に、キューは操作です。制約の線形リストに従います。挿入操作の最後をキューの末尾と呼び、削除操作の最後をキューの先頭と呼びます。
ダブルポインターでマークし、フロントポインターとリアポインターを介してキューの先頭と末尾をマークし、フロントポインターとリアポインターの位置でのみ追加、削除、変更、およびクエリを許可することができます。アレイ上で実現されます。これは、配列のデータ構造を使用したキューのシミュレーションです。初心者は、まずこの方法でキューに慣れることをお勧めします。
具体操作:
/* 通常、フロントには 0 の値が割り当てられ、リアには -1 の値が割り当てられ、 最初の要素の後続のエントリ、終了、および取得が容易になります */ int a[100], front=0, rear=- 1; // 入力 a[+ +rear] = 10; // フロント ++をデキュー // 最初の要素を取得 a[フロント] // テール要素を取得 a[リア] // 空かどうかを判断 if(フロント > リア) cout << "空のチームのキュー";
ただし、後の段階では、時間を節約するために、C++ に付属の STL コンテナーを直接使用して操作を完了することができます。
具体的な操作は次のとおりです。
// キュー パッケージをインポートします #include<queue> // キュー オブジェクトを宣言します // 埋めたいデータ型を入力します queue<int> qu; // キューを入力します int a = 10; qu.push(a) ; // キュー アウト、戻り値なし qu.pop(); // 最初の要素を取得 int front = qu.front(); // 末尾の要素を取得 int rear = qu.back(); // チームかどうかis empty if( qu.empty()) { cout << "キューは空です"; } // キューのサイズ、戻り値 int qu.size();
5. テンプレート
BFS のトピックには、明らかなテンプレートのセットがあります. 信じられない場合は、見てみましょう. (コアコードが表示されます)
while(!q.empty())//q非空,可以走
{
for(int i=0;i<4;i++)//四个方向
{
ma ac=q.front();
int nx = ac.x0 + dx[i];
int ny = ac.y0+ dy[i];
if(nx>=1&&ny>=1&&nx<=3&&ny<=3)
{
swap(ac.a[ac.x0][ac.y0],ac.a[nx][ny]);
ac.x0=nx;
ac.y0=ny;
//将0与目标数交换
ac.ans++;//步数加1
ac.kt=kt(ac);
//康托判重
if (!flag[ac.kt])
{
flag[ac.kt] = 1;
q.push(ac);
//加入队列
if(ac.kt==mo.kt)
{
printf("%d",q.back().ans);
exit(0);
}
}
}
}
q.pop();
//弹出已遍历完所有情况的矩阵
}
while(!q.empty())
{
for(int i=0;i<4;i++)
{
node ne;
ne.x=q.front().x+dx[i],ne.y=q.front().y+dy[i];
if(bo[ne.x][ne.y]==0&&ne.x>=0&&ne.y>=0&&ne.x<=n&&ne.y<=m&&a[ne.x][ne.y]==1)
{
ne.ans=q.front().ans+1;
bo[ne.x][ne.y]=1;
q.push(ne);
if(ne.x==xe.x&&ne.y==xe.y)
{
flag=true;
printf("%d\n",q.back().ans);
break;
}
}
}
if(flag) break;
q.pop();
}
while(scanf("%d%d%d%d",&t.a[1],&t.a[2],&t.a[3],&k)!=EOF)//多组数据
{
flag=false;
memset(bo,0,sizeof(bo));
bo[t.a[1]][t.a[2]][t.a[3]]=1;
p.ans=0;
p.a[1]=t.a[1];
p.a[2]=p.a[3]=0;
q.push(p);
if(q.front().a[1]==k||q.front().a[2]==k||q.front().a[3]==k)
{
printf("yes\n%d\n",0);
continue;
}//特判
while(!q.empty())
{
for(int i=1;i<=3;i++)
{
if(q.front().a[i]>0)
{
for(int j=1;j<=3;j++)
{
node sy;
sy=q.front();
sy.ans ++;
if(i!=j)
{
int T=t.a[j]-sy.a[j];
if(sy.a[i]>T)
{
sy.a[j]=t.a[j];
sy.a[i]=sy.a[i]-T;
}
else
{
sy.a[j]+=sy.a[i];
sy.a[i]=0;
}
if(!bo[sy.a[1]][sy.a[2]][sy.a[3]])
{
bo[sy.a[1]][sy.a[2]][sy.a[3]]=1;
if(sy.a[1]==k||sy.a[2]==k||sy.a[3]==k)
{
flag=true;
}
q.push(sy);
}
}
}
}
}//遍历每一种倒法
if(flag) break;
q.pop();
}
if(flag)
{
printf("yes\n%d\n",q.back().ans);
}
else
{
printf("no\n");
}
while(!q.empty())
{
q.pop();
}
}
あまり似ていないトピックをいくつか選びましたが、よく見ると構造は同じです。
これは、ステップ図として表すことができます。
擬似コードでは、次のように表現されます。
int BFS(Node start, Node target) {
入队(初始状态);
visited[初始状态] = true;
while(!空队) {
for() { // 状态转换
Node node = 队首元素;
对node的处理,生成新node状态;
if (visited[node] == true)
continue;
if (node == target) {
输出答案;
return 0;
}
v[node] = true;
入队(node);
}
出队();
}
}
これは BFS 問題のテンプレートです。! !
6.最後に
何もありません。気に入ったら、スリー リンクを付けてください。あなたのサポートは私の最大の動機です!