1. 程式人生 > 實用技巧 >AcWing: BFS相關

AcWing: BFS相關

AcWing: BFS相關

背景知識:有向圖

離散數學圖論中的概念,由點和邊組成圖,其中圖中所有邊都是帶有方向的圖稱為有向圖。

在演算法題中,我們可以用鄰接矩陣和鄰接表(推薦)儲存有向圖。

鄰接表即類似於雜湊表的拉鍊法,用連結串列來解決衝突問題。並且其中的連結串列順序並不重要(儲存相鄰點),對於解決bfs問題來說。

寬搜框架

例題一:圖中點的層次

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100010;
//兩層節點體系:一層為idx;一層為有向圖中點的編號存在e[N]中

int n, m;
int h[N], e[N], ne[N], idx; //h[N]為雜湊表,e[N]用來存節點指向的節點在有向圖中的編號,ne[N]為領接表上的next,idx為當前用到的節點數
int d[N], q[N]; //q[N]為佇列儲存bfs搜尋過程,d[N]標識點是否被搜尋過,記錄到點1的距離

void add(int a, int b) //將邊a指向b加入鄰接表中
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int bfs()
{
    int hh = 0, tt = 0;//初始化queue佇列
    q[0] = 1;
    
    memset(d, -1, sizeof d);
    
    d[1] = 0;
    
    while(hh <= tt)
    { 
        int t = q[hh ++];//bfs用佇列彈出跳到下一層搜
        
        for(int i = h[t]; i != -1; i = ne[i])//bfs一般都是用for在當前層搜
        {
            int j = e[i];
            if(d[j] == -1)
            {
                d[j] = d[t] + 1;//距離加1,可以得到點1到所有點的距離,儲存在陣列d[N]中,也完成了這一層的搜尋
                //對當前層來說t的值是固定的,所以是按層搜尋,下一層t的值會變為隊頭的元素值,之前的隊頭已經出隊了
                q[++tt] = j;
            }
        }
    }
    
    return d[n];
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);//初始化h[N]雜湊表,-1代表指向為空null
    
    for(int i = 0; i < m; i ++) //存入有向圖
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }
    
    cout << bfs() << endl;
    
    return 0;
}

例題二:拓撲序列

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 100010;

int n, m;
int h[N], e[N], ne[N], idx;
int q[N], d[N];//d[N]記錄有向圖中編號為i的點的入度

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool topsort()
{
    int hh = 0, tt = -1;//tt表示佇列中加入的點數-1
    
    for(int i = 1; i <= n; i ++)//把入度為0的點加入佇列中
        if(!d[i]) q[++tt] = i;
        
    while(hh <= tt)
    {
        int t = q[hh++];//用彈出隊頭來搜尋下一次層
        for(int i = h[t]; i != -1; i = ne[i])//用for來進行當前層搜尋
        {
            int j = e[i];
            d[j] --; //以入度減1表示該點搜過了,看習題課尋求答疑
            if(d[j] == 0) q[++tt] = j; //將入度為0的點加入佇列
        }
    }
    
    return tt == n - 1;//如果為true,代表n個點全部加入了佇列中
}

int main()
{
    cin >> n >> m;
    
    memset(h, -1, sizeof h);
    
    for(int i = 0; i < m; i ++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        d[b] ++;//順帶記錄入度
    }
    
    if(topsort())
    {
        for(int i = 0; i < n; i++) printf("%d ", q[i]);
        puts("");
    }
    else puts("-1");
    
    return 0;
}