今回はEDPCのG問題です。 再帰を使う問題です。
問題
頂点辺の有向グラフがある。 の頂点はと番号が振られており、 番目の有向辺は 頂点からへと張られている。
の有向パスのうち、最長のものの長さを求めよ。
条件
- は有向閉路を含まない
考えたこと
- := 頂点から始まる有向パスのうち、最長のものの長さ
と、とりあえずしてみることはすぐに考えられました。 問題はこれをどのように求めるかです。
ここで、頂点からの有向辺が1つのみ存在し、 それが頂点に伸びているとします。 このとき、の値が分かっていれば、 \begin{align} DP_{u} = DP_{v} + 1 \end{align} が成立します。
ということは、頂点から伸びている全ての有向辺について拡張させて、 \begin{align} DP_{u} = \left( \max_{v_{i} \in v} DP_{v_{i}} \right) + 1 \end{align} が成立します。
あとはこれを実装するのみですが、 各頂点について毎回伸びている辺について調べるには、あまりにも時間がかかります。
そこで、再帰的にの値を求めることを考えます。 そして頂点について、既にの値を調べ上げていれば、 何度も計算する必要はありません。
以上から、各頂点について、既に調べたかどうかを表すbool型の配列を用意して、
- すでに調べていれば、の値を返す
- 調べてなければ、再帰的にの値を求める
という再帰関数を書けば、各辺を1回ずつ辿るのみなので、 で計算できるはずです。
ソースコード
#include <bits/stdc++.h> using namespace std; int N, M; vector <int> Graph[int(1e5+5)]; // グラフの入る配列 int DP[int(1e5+5)]; bool visited[int(1e5+5)]; // DP[num]の値を求める再帰関数 int Solve(int num){ // もしすでに調べていればDP[num]を返す if (visited[num]) return DP[num]; // 訪問済にする visited[num] = true; // numの有向辺の到着先のDPの値を順に求めていく int ans = 0; for (int next : Graph[num]){ // 次の頂点での値 + 1 の最大値が答えになる ans = max(ans, Solve(next) + 1); } // DP[num] = ansということを代入し、ansの値を返す return DP[num] = ans; } int main(){ cin >> N >> M; for (int i = 0; i < M; i++){ int X, Y; cin >> X >> Y; Graph[X].push_back(Y); } // 頂点1から頂点Nの値までDP[i]の値を求めていく int ans = 0; for (int i = 1; i <= N; i++){ ans = max(ans, Solve(i)); } cout << ans << endl; return 0; }
ACしたコード: Submission #16633084 - Educational DP Contest
感想
この問題、最初はいろんな人のコードを見てもわからなかったです。
グローバル変数という概念をここから知りました。