牛ゲー

概要

\(N\) 個の変数 \(x_i\), \(M\) 個の 差分制約式(difference constraints) \(x_j - x_i \le b_k\) の元で、2つの変数からなる式 \(x_t - x_s\) を最大化する問題。 この問題は通称「牛ゲー」と呼ばれている。

この問題は 2頂点間の最短経路問題 の双対問題であり、 Bellman-Ford Algorithm で解くことができる。

変数 \(x_i\) を 頂点 \(v_i\) に対応させ、差分制約式 \(x_j - x_i \le b_k\) を 重み \(b_k\) を持つ \(v_i\) から \(v_j\) への有向辺に対応させた グラフを構築する。
このグラフ上で、\(v_s\) を始点として \(v_t\) への最短路を求める。

負閉路がある場合は解なし。
それ以外の場合であれば 頂点 \(v_s\) からの 頂点 \(v_t\) までの最短距離が \(x_t - x_s\) の最大値と一致する。
頂点 \(v_s\) から 頂点 \(v_t\) まで到達できない場合は \(x_t - x_s\) はいくらでも大きくできることになる。

計算量

\(O(NM)\)

実装

INF = 10**9
def calc(N, E, s):
    G = [[] for i in range(N)]
    for a, b, d in E:
        # x_a - x_b <= d
        G[b].append((a, d))

    INF2 = INF**2
    dist = [INF2] * N
    dist[s] = 0
    update = 1
    for _ in range(N):
        update = 0
        for v in range(N):
            for w, d in G[v]:
                if dist[v] + d < dist[w]:
                    dist[w] = dist[v] + d
                    update = 1
        if not update:
            break
    else:
        return None
    for i in range(N):
        dist[i] = min(dist[i], INF)
    # dist[i]: the maximum value of (x_i - x_s)
    return dist

N = 3
# x_1 - x_0 <= 3
# x_2 - x_1 <= 5
# x_2 - x_0 <= 10
E = [(1, 0, 3), (2, 1, 5), (2, 0, 10)]
print(calc(N, E, 0))
# => "[0, 3, 8]"

Verified

  • AOJ: "0304: School Cafeteria": source (Python3, 0.41sec)