【強連通分量】Tarjan(縮點)POJ1236-Network of Schools

題意:
N個點的有向圖G,若有路從u通向v,則稱u可達v(v不一定可達u)。現給出各點之間的連接關係。
Q1:求出至少從幾個點出發,才能使得所有的點均可以遍歷一遍;
Q2:至少需要添加幾條單向邊,才能使得從這些點中任何一個出發都可以遍歷全部點(使G強連通)。

分析:
和最近學的離散數學有關。這個題目可以用tarjan求圖G強連通分量(縮點)。如果只有一個強連通分量則圖G強連通,任意兩點可達,如果有多個強連通分量,則考察這些分量(縮點後成爲新圖結點),考慮這些點中入度爲0的點:

i )入度爲0的點不能被其他點到達,而一個入度不爲0的點可以從某個入度爲0的點到達,從這些入度爲0的點出發即可遍歷全部結點;。
ii)出度爲0的點,在圖G中,入度爲0的點(設爲m個)無法從其他點到達,那麼爲了使得所有的點連通,需要m條路徑連接到這m個入度爲0的點;而出度爲0的點(設爲n個)無法到達其他點,那麼爲了使得所有的點連通,需要n條路徑從這n個出度爲0的點連出。於是,至少需要添加 max(m, n)條邊,使得圖中所有的點的入度和出度不爲0.

同時,在一個DAG中,如果該圖的所有點均可連接到一塊,且每個點的出度和入度均不爲0,則該圖肯定強連通。於是,結果爲 max(m,n)。

代碼如下:

#include <bits/stdc++.h>
const int N = 105;
int sccno[N],dfn[N],low[N],scc_cnt,dfs_clock;
vector<int> G[N];
stack<int> s;
//自定義
int n;
int sum[N],point[N];
int _in[N],_out[N];
//
void dfs(int u){
    dfn[u] = low[u] = ++dfs_clock;
    s.push(u);
    for(int i=0;i<G[u].size();i++){
        int v = G[u][i];
        if(!dfn[v]){
            dfs(v);
            low[u] = min(low[u],low[v]);
        }else if(!sccno[v]){
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(dfn[u] == low[u]){
        scc_cnt++;
        int tot = s.size();
        for(;;){
            int x = s.top();
            s.pop();
            sccno[x] = scc_cnt;
            if(x==u)break;
        }
        sum[scc_cnt] = tot - s.size();//縮點
    }
}

void tarjan(int n){
    memset(dfn,0,sizeof(dfn));
    for(int i=1;i<=n;i++){
        if(!dfn[i])dfs(i);
    }
}

void solve(){
    scc_cnt = dfs_clock = 0;
    for(int i=0;i<=n;i++)G[i].clear();
    for(int i=1;i<=n;i++){
        int x;
        while(cin>>x&&x){
            G[i].push_back(x);
        }
    }
    tarjan(n);
    memset(_in,0,sizeof(_in));
    memset(_out,0,sizeof(_out));
    for(int u=1;u<=n;u++){
        for(int j=0;j<G[u].size();j++){
            int v = G[u][j];
            if(sccno[u] != sccno[v]){
                _in[sccno[v]] ++;//縮點後求入度出度
                _out[sccno[u]] ++;
            }
        }
    }
    int deg_in = 0,deg_out = 0;
    for(int i=1;i<=scc_cnt;i++){
        if(_in[i] == 0)deg_in ++;
        if(_out[i] == 0)deg_out ++;
    }
    //輸出結果
    if(scc_cnt == 1){//只有一個強連通分量,原圖強連通 
        printf("1\n0\n");
    }
    else{
        printf("%d\n%d\n",deg_in,max(deg_in,deg_out));
    }

}

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);

    while(cin>>n){
        solve();
    }
    return 0;
}
發佈了110 篇原創文章 · 獲贊 30 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章