読者です 読者をやめる 読者になる 読者になる

srupのメモ帳

競プロで解いた問題や勉強したことを記録していくメモ帳

aoj GRL_3 Connected Components - Strongly Connected Components

問題

問題概要

強連結成分分解する問題。

解法

強連結成分を分解するアルゴリズムとして、Kosarajuというものがある。今回はそれを使って、実装した。アルゴリズム自体は下のサイトを見てもらえれば、分かりやすい。

mathtrain.jp

ミス

アルゴリズム自体の理解が不十分。なんとなく、2回dfsすれば、いいのはわかるけど。

コード

#include <iostream>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
#define rep(i,n) for(int i=0;i<(n);i++)

///強連結成分分解 (Kosaraju)///
#define MAX_V 10000//頂点数
int V;
vector<int> G[MAX_V], rG[MAX_V];
vector<int> vs;
bool used[MAX_V];
//cmp[v] = cmp[U]なら、頂点u, vは同じ強連結成分
int cmp[MAX_V];//cmp[v] := 頂点vが含まれる連結成分がどれなのかを示す番号
//隣接リストを作る
void add_edge(int from, int to){//0origin
    G[from].push_back(to);//与えられた有向グラフの隣接リスト
    rG[to].push_back(from);//与えられたグラフの矢印を逆した有向グラフの隣接リスト
}
//一度目のdfs
void dfs(int v){
    used[v] = true;
    for (int i = 0; i < G[v].size(); ++i){
        if(!used[G[v][i]]) dfs(G[v][i]);
    }
    vs.push_back(v);//これ以上進めなくなったものから順にvsに頂点番号を入れていく
}
//2度目のdfs
void rdfs(int v, int k){
    used[v] = true;
    cmp[v] = k;//頂点vに対して、k番目と強連結成分であること入れる
    for (int i = 0; i < rG[v].size(); ++i){
        if(!used[rG[v][i]]) rdfs(rG[v][i], k);
    }
}
int scc(){
    memset(used, 0, sizeof(used));//0(使ってない)で初期化
    vs.clear();//初期化
    for (int v = 0; v < V; ++v){
        if(!used[v]) dfs(v);
    }
    memset(used, 0 , sizeof(used));
    int k = 0;//強連結成分を分ける番号
    for (int i = vs.size() - 1; i >= 0; --i){//vsに入っている後ろのものからdfs
        if(!used[vs[i]]){
            rdfs(vs[i], k); k++;
        }
    }
    return k;
}

int main(void){
    int E, s, t;
    cin >> V >> E;
    rep(i, E){
        cin >> s >> t;
        add_edge(s, t);//頂点sとtを辺として繋ぐ s -> t
    }
    scc();//強連結成分を作る
    int q, u, v; cin >> q;
    rep(i, q){
        cin >> u >> v;
        if(cmp[u] == cmp[v]) printf("1\n");
        else printf("0\n");
    }
    return 0;
}