0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

強連結成分分解の実装(C++)

Last updated at Posted at 2021-03-05

1. 概要

1-1. 趣旨

強連結成分分解とは?という話は色々なサイトに書かれているが、実装して、コードを1行ずつ解説してくれる記事がなかった。自分の理解を兼ねて、コードに1行ずつ解説を付けたものを貼る。

1-2. 概念自体の解説記事

こちらが非常にわかりやすく感じた。
https://mathtrain.jp/kyorenketsu

1-3. 使用した問題

Aizu Online Judge GRL-3C
https://onlinejudge.u-aizu.ac.jp/courses/library/5/GRL/3/GRL_3_C

2. 実装

C++17で実装。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll //int:2*10**9
typedef long double ld;
typedef pair<ll,ll> P;
#define REP(i,n) for(ll i = 0; i<(ll)(n); i++)
#define FOR(i,a,b) for(ll i=(a);i<=(b);i++)
#define FORD(i,a,b) for(ll i=(a);i>=(b);i--)
#define pb push_back
#define MOD 1000000007 //998244353
#define PI 3.141592653
#define INF 100000000000000 //14
//cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);
#define N 11000

vector<vector<pair<ll,bool>>> conn(N);
vector<ll> checked(N,0); // dfs参照
vector<ll> orderList(N,-1); // dfsで通った順番に登録していく
vector<ll> groupList(N,-1); // 各nodeにgroupをつけていく
ll cnt = 0;

void dfs(ll now, ll prev) { 
    // 帰り道に通った順に番号をつける, 重複が起きないように行きにcheckedする
    // orderList[i]はi番のnode
    checked[now]=1;
    for (auto v : conn[now]) {
        // すでに見たところ、親、false(逆向きの枝)
        if (checked[v.first]==1 || v.first==prev || v.second==false) continue;
        dfs(v.first,now);
    }
    // 通った順に番号をつける
    orderList[cnt]=now; cnt++;
    return;
}


void dfs2(ll now, ll group) {
    // nowが今見るnode, groupが登録するgroup番号
    groupList[now]=group;
    for (auto v : conn[now]) {
        // すでにgroup番号が付いてるnode、逆向きでない枝はスルー
        if (groupList[v.first]!=-1 || v.second==true) continue;
        // nowから行けるところに、同じgroup番号をつける
        dfs2(v.first,group);
    }
    return;
}

int main(){
    ll n, m; cin >> n >> m;
    REP(i,m) {
        ll u, v; cin >> u >> v;
        // trueで登録するのは元の向きの枝
        // falseは逆向きの枝(dfs2で用いる)
        conn[u].pb({v,true});
        conn[v].pb({u,false});
    }
    REP(i,n) if (!checked[i]) {
        // 連結グラフでない場合には開始点が複数個必要
        dfs(i,-1);
    }
    FORD(i,n-1,0) {
        // -1のときはそれ以降も全部-1なのでbreak
        // 単独のnodeがある場合は、orderListにn個全部が登録されないので、あまりの枠が出る
        if (orderList[i]==-1) break;
        // すでに見たnodeの場合はgroupListにgroup番号が登録されているので、スルー
        if (groupList[orderList[i]]!=-1) continue;
        // 始点となるnodeの番号をグループ番号とする
        dfs2(orderList[i],orderList[i]);
    }
    ll q; cin >> q;
    REP(i,q) {
        ll u, v; cin >> u >> v;
        // 同じグループに属する場合は1, そうでなければ0
        if (groupList[u]==groupList[v]) cout << 1 << endl;
        else cout << 0 << endl;
    }
    return 0;
}

3. クラス化しておく

struct SCC {
    ll n;
    vector<vector<pair<ll,bool>>> conn;
    vector<ll> checked;
    vector<ll> orderList;
    vector<ll> groupList;
    ll cnt = 0;
    SCC(ll n) : n(n), conn(n), checked(n,0), orderList(n,-1), groupList(n,-1) {};
    void dfs(ll now, ll prev) {
        checked[now]=1;
        for (auto v : conn[now]) {
            if (checked[v.first]==1 || v.first==prev || v.second==false) continue;
            dfs(v.first,now);
        }
        orderList[cnt]=now; cnt++;
        return;
    }
    void dfs2(ll now, ll group) {
        groupList[now]=group;
        for (auto v : conn[now]) {
            if (groupList[v.first]!=-1 || v.second==true) continue;
            dfs2(v.first,group);
        }
        return;
    }
    void make_group() {
        REP(i,n) if (!checked[i]) dfs(i,-1);
        FORD(i,n-1,0) {
            if (orderList[i]==-1) break;
            if (groupList[orderList[i]]!=-1) continue;
            dfs2(orderList[i],orderList[i]);
        }
    }
};

4. 終わりに

アルゴリズムの解説記事が多い割には、実装まで面倒見よく解説してくれる記事ってあまりなくて、もっとあったら嬉しいなとよく思ってたので、今回勉強ついでに書いてみた。(意外にこういうのって需要あると思うんですが、どうなのでしょうか。)

ここをこう書いたほうがスマートじゃないか?などの指摘があれば是非コメントなど頂けると幸いです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?