okura diary

おもに競技プログラミングの日記

JAG 模擬国内2020 H 辺が先か,頂点が先か (AOJ 3208 Edge or Vertex)

問題文 : Aizu Online Judge

LPの定式化から2つのアプローチができて面白かったのでそれについて説明する。

考察1

先手が初めに各辺e \in Eに確率p_eを割り振り、後手はそれに応じてv \in Vを確率q_vで選ぶとする。値 X = \sum_{e=(u,v)\in E} p_e(q_u - q_v) を先手は最大化、後手は最小化する時、Xの値はいくらかという問題になる。 まず後手はあるpure strategy(ある選択肢を確立1で選ぶ)が最適になる。すなわち、先手の戦略pに対し、各頂点vごとに\sum_{e \in \delta_-(v)}p_e - \sum_{e \in \delta_+(v)}p_eを計算し、これが最小となる頂点を確立1で選ぶのが後手の最適な戦略である。この考察を踏まえると、次のようにLPで定式化できる。


maximize : min_{v \in V} \sum_{e \in \delta_-(v)}p_e - \sum_{e \in \delta_+(v)}p_e \\
s.t. \\
p_e \geq 0 \\
\sum p_e = 1

考察2

与えられるグラフに閉路が存在する場合、pは閉路の辺に均等に確率を割り振ればXの値を0にできる。また、後手はすべての頂点に均等に確立を割り振る戦略でXの値を必ず0以下にできる。 よって、グラフに閉路が存在するなら、解は0が言える。

そうでないとき、\sum_{e \in \delta_-(v)}p_e - \sum_{e \in \delta_+(v)}p_eが負になる頂点が少なくとも1つは存在する。よってDAGの時、解は負の数が言える。

LP解法1

考察1での定式化では、maximizeの対象にminが入っており扱いづらい。こういう際の常套手段として新しい変数を加えminを不等式制約で表現するというのがある。それを行うと、


maximize : S \\
s.t. \\
p_e \geq 0 \\
\sum p_e = 1\\
S \leq \sum_{e \in \delta_-(v)}p_e - \sum_{e \in \delta_+(v)}p_e (\forall v \in V)

自分はここで詰まってしまった。双対とっても綺麗になりそうもないし(もしかしたらなるかもしれませんが)いかんせん \sum p_e = 1の制約が邪魔で変形や解釈が難しかった。

ここ(Lesson 35: Game Theory and Linear Programming)のスライドを眺めてみると、Sの符号がわかっているときは(より一般にmatrix gameでできる)いい感じの変形があるということがわかった。今回の場合、考察2からDAGで解が負になる場合だけを考えればよいためこの変形が利用できる。 具体的には x_e = p_e / |S|とおくと、


maximize : S \\
s.t. \\
x_e \geq 0 \\
\sum x_e = 1 / |S|\\
-1 \leq \sum_{e \in \delta_-(v)}x_e - \sum_{e \in \delta_+(v)}x_e (\forall v \in V)

となるが、よく見るとSが消去できることがわかる。よって問題は


maximize : \sum x_e \\
s.t. \\
x_e \geq 0 \\
\sum_{e \in \delta_+(v)}x_e - \sum_{e \in \delta_-(v)}x_e \leq 1(\forall v \in V)

となる。これは「各頂点を始点とするパスの長さの合計を最大化」するグラフの問題と解釈でき、今回グラフはDAGなので各頂点までの最長パスはDPで簡単に求まるので解けた。 (解説にはラグランジュ双対を取ると書いてあるけど和が1の制約とかが邪魔でどうやったら取れるのかわからない...誰か教えてください)

LP解法2

LPで定式化した際に現れる\sum_{e \in \delta_-(v)}p_e - \sum_{e \in \delta_+(v)}p_eという式は各頂点への相対流入量を表しているので、 p_eをただの辺に割り振られた確率とみるのではなくフローとみなすことが有効かもしれないことがわかる。すなわち、「各頂点の相対流入量がS以上ですべての辺の合計流量が1となるようなフローが存在するSの最大値をもとめる」問題だと解釈できる。ここでそのようなフローが存在するかどうかはSに関して単調であるので、二分探索をすることにすると次の問題を解きたくなる。


maximize : \sum p_e \\
s.t. \\
p_e \geq 0\\
\sum_{e \in \delta_-(v)}x_e - \sum_{e \in \delta_+(v)}x_e \geq S (\forall v \in V)

双対をとると


minimize : -S(\sum q_v) \\
s.t. \\
q_v \geq 0\\
q_u - q_v \geq 1 ((u,v) \in E)

となり、実はSは無関係で二分探索が必要ないことがわかる。答えはこの問題の最適解での -\dfrac{1}{\sum q_v}となる。(合計流量を1以上にできる境目を見つけたいので) そして上の問題はいわゆる牛ゲー(差分制約による最短(長)経路問題のポテンシャルの最大(小)化)に近い形であり、各頂点からの最長路の長さの和が最適値であることがわかる。よって解けた。

提出

Aizu Online Judge

bool solve() {
  int N, M;
  cin >> N >> M;
  if (N == 0 && M == 0) return false;
  vector<vector<int>> g(N);
  for (int i = 0; i < M; i++) {
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    g[a].push_back(b);
  }
  vector<int> vs;
  vector<bool> used(N, false);
  vector<int> ord(N);
  function<void(int)> dfs = [&](int v) {
    used[v] = true;
    for (int u : g[v]) {
      if (!used[u]) dfs(u);
    }
    vs.push_back(v);
  };
  for (int i = 0; i < N; i++) {
    if (!used[i]) dfs(i);
  }
  reverse(all(vs));
  for (int i = 0; i < vs.size(); i++) { ord[vs[i]] = i; }
  for (int i = 0; i < N; i++) {
    for (int j : g[i]) {
      if (ord[i] > ord[j]) {
        cout << 0 << endl;
        return true;
      }
    }
  }
  vector<int> dp(N, 0);
  for (int i = N - 1; i >= 0; i--) {
    int v = vs[i];
    for (int u : g[v]) { chmax(dp[v], dp[u] + 1); }
  }
  cout << setprecision(30) << -1.0 / (long double)(accumulate(all(dp), 0))
       << endl;
  return true;
}