競技プログラミング日記

主に AtCoder の記事です

第七回 アルゴリズム実技検定 J問題

 

PAST007J

有向グラフのサイクル検出
トポロジカルソートと同じ.DFS,BFSでも可能.

DFSによる実装
頂点 \(i\) に対して, \(val_{i}\) を

  • 0 : 訪れてない
  • 1 : 訪れたが計算途中
  • 2 : 訪れて計算が終わった

として保持する. \(val_{i} = 1\) の状態で再び訪れたらサイクルということ.
遷移するとき,if(ne != pa)の部分は不要.

BFSによる実装
入次数を保持しておく. 頂点に訪れたら,そこに入った辺は二度と使わないので,入次数を-1する. 入次数が0になった頂点を追加していく.
トポロジカルソート可能であることは, BFSが終わった時点で全ての頂点を訪れているのが必要十分.
実装上の注意
サイクルの長さが1,2の場合はコーナーケースになりやすい.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n,m; cin >> n >> m;
  vvll to(n);
  rep(i,m){
    ll a, b; cin >> a >> b;
    --a, --b;
    to[a].push_back(b);
    //to[b].push_back(a);
  }

  vll val(n);
  auto dfs = [&](auto f, ll cu, ll pa = -1) -> bool {
    if(val[cu] == 1){
      return true;
    }else if(val[cu] == 2){ // 枝狩
      return false;
    }
    val[cu] = 1;

    for(ll ne: to[cu]){ // ne != pa は不要
      if(f(f, ne, cu)) return true;
    }
    val[cu] = 2;
    return false;
  };

  rep(i,n) if(val[i] == 0){
    if(dfs(dfs, i)){
      cout << "Yes" << endl;
      return 0;
    }
  }

  cout << "No" << endl;
 

  return 0;
}