Day 162-Q5 — 2026-09-27 — 牛ゲー(差分制約系を最短路で解く。始点から届かない負閉路も「解なし」と判定する)

2026-09-27 赤色 Master (Phase 8+) ★★★★★★★★★ グラフ(焦点帯外 M2)。$x_b-x_a\le d$ の連立不等式を辺 $a\to b$(重み $d$)に置き換え、「最大化=最短路」「矛盾=負閉路」「無限大=到達不能」を 1 本の Bellman–Ford で読み分ける

問題

$N$ 頭の牛が番号順に一直線に並ぶ。牛 $i$ の位置を整数 $x_i$ とし、$x_1\le x_2\le\dots\le x_N$ でなければならない(同じ位置に複数頭いてよい)。さらに次の条件がある。

  • 仲良しの組 $ML$ 個: 牛 $a,b$($a<b$)は距離 $d$ 以内、すなわち $x_b-x_a\le d$
  • 仲の悪い組 $MD$ 個: 牛 $a,b$($a<b$)は距離 $d$ 以上、すなわち $x_b-x_a\ge d$

すべての条件を満たす配置のうち、$x_N-x_1$ の最大値を出力せよ。条件を満たす配置が存在しなければ -1、$x_N-x_1$ をいくらでも大きくできるなら -2 を出力せよ。

入力形式

N ML MD
a_1 b_1 d_1        (仲良しの組 ML 行)
...
a_1' b_1' d_1'     (仲の悪い組 MD 行)
...

制約

#条件
1$2\le N\le 1000$
2$0\le ML, MD\le 5000$
3$1\le a<b\le N$
4$0\le d\le 10^6$
5入力はすべて整数

入出力例

入力例1

4 2 1
1 3 10
2 4 20
2 3 3

出力例1

27

($x_3\le x_1+10$、$x_4\le x_2+20$、$x_2\le x_3-3$ から $x_4\le x_1+27$。$x=(0,7,10,27)$ で達成)

入力例2

3 1 2
2 3 1
2 3 4
2 3 2

出力例2

-1

($x_3-x_2\le1$ と $x_3-x_2\ge4$ が矛盾。牛 1 からはこの矛盾に辿り着けない ので、牛 1 を始点にした Bellman–Ford だけだと -2 と誤答する)

入力例3

3 1 0
1 2 5

出力例3

-2

(牛 3 を縛る条件が無いので $x_3$ はいくらでも右に置ける)

背景と出典

「牛ゲー」は秋葉・岩田・北川『プログラミングコンテストチャレンジブック 第 2 版』(2012)2-5 節の例題「Layout」(POJ 3169)に由来する通称で、差分制約系(difference constraints)を最短路に帰着する定番。差分制約と最短路の対応は Cormen ら『Introduction to Algorithms』の「Difference constraints and shortest paths」節でも扱われる。本セッションでは両書を参照しておらず、正しさは下の検証ログ(全探索と Floyd–Warshall との照合)を根拠にしている。
本問は原題に「牛 1 から届かない矛盾」を検出する要件を明示的に入れた(入力例 2)。

概念図:不等式のグラフと最短路・負閉路

入力例1: x_v ≤ x_u + c を辺 u→v(重み c)に。頂点 1 → 4 の最短路 = 最大の x₄−x₁ 0 0 0 1→3:10(x₃−x₁ ≤ 10) 2→4:20(x₄−x₂ ≤ 20) 3→2:−3(x₃−x₂ ≥ 3) 1 2 3 4 最短路 1→3→2→4 = 10 + (−3) + 20 = 27(黄色の辺)= 出力 27 入力例2:届かない負閉路1231−4閉路 1+(−4) < 0 並び順 xᵢ ≤ xᵢ₊₁ は辺 i+1→i(重み 0、点線) 頂点 1 からは 2・3 へ辺が無く、閉路は ∞ のまま見逃される → 1 回目の Bellman–Ford は全頂点を距離 0 で始める

ヒント(クリックで段階的に開示)

ヒント1(方向性)

$x_b\le x_a+d$ は、最短路の距離がみたす三角不等式 $\mathrm{dist}(b)\le\mathrm{dist}(a)+w(a,b)$ と同じ形。すべての条件を「$x_v-x_u\le c$」の形にそろえてみよう。

ヒント2(アプローチ)
  • 仲良し $x_b-x_a\le d$ → 辺 $a\to b$、重み $d$
  • 仲の悪い $x_b-x_a\ge d\iff x_a-x_b\le -d$ → 辺 $b\to a$、重み $-d$
  • 並び順 $x_i\le x_{i+1}\iff x_i-x_{i+1}\le0$ → 辺 $i+1\to i$、重み $0$

頂点 1 からの最短距離 $\mathrm{dist}(N)$ が $x_N-x_1$ の最大値。

ヒント3(誘導)

Bellman–Ford を 2 回使う。
1. 全頂点の距離を 0 で始めて $N$ 周緩和し、$N$ 周目も更新があれば負閉路=解なし(-1)。これはどこにある負閉路でも見つける
2. 頂点 1 だけ 0、他を $\infty$ で始めて最短路。$\mathrm{dist}(N)=\infty$ なら -2、そうでなければ $\mathrm{dist}(N)$

模範解答 (Python)

模範解答を表示する(まず自力で解いてから)
import sys

INF = float("inf")


def bellman_ford(n, edges, dist):
    """dist を初期値として緩和を最大 n 周。n 周目にまだ更新されたら負閉路あり(False を返す)"""
    for it in range(n):
        updated = False
        for u, v, w in edges:
            du = dist[u]
            if du != INF and du + w < dist[v]:
                dist[v] = du + w
                updated = True
        if not updated:
            return True
    return False


def main():
    data = sys.stdin.buffer.read().split()
    n, ml, md = int(data[0]), int(data[1]), int(data[2])
    edges = []
    p = 3
    for _ in range(ml):                     # x_b - x_a <= d  →  辺 a→b 重み d
        a, b, d = int(data[p]) - 1, int(data[p + 1]) - 1, int(data[p + 2]); p += 3
        edges.append((a, b, d))
    for _ in range(md):                     # x_b - x_a >= d  ⇔  x_a - x_b <= -d  →  辺 b→a 重み -d
        a, b, d = int(data[p]) - 1, int(data[p + 1]) - 1, int(data[p + 2]); p += 3
        edges.append((b, a, -d))
    for i in range(n - 1):                  # x_i <= x_{i+1}  ⇔  x_i - x_{i+1} <= 0  →  辺 i+1→i 重み 0
        edges.append((i + 1, i, 0))
    # 1) 解があるか: 全頂点を距離 0 で始める(仮想の始点から重み 0 の辺を全頂点へ張ったのと同じ)
    if not bellman_ford(n, edges, [0] * n):
        print(-1)
        return
    # 2) 最大の x_N - x_1 = 頂点 1 から頂点 N への最短距離
    dist = [INF] * n
    dist[0] = 0
    bellman_ford(n, edges, dist)            # 1) で負閉路が無いと分かっているので必ず収束する
    print(-2 if dist[n - 1] == INF else dist[n - 1])


main()

計算量: $O(N\cdot(ML+MD+N))$。実測(Python 3.14.7、M1 Mac)で $N=1000$、$ML=MD=5000$、Bellman–Ford がほぼ $N$ 周回るように辺を逆順に並べた最悪ケースが 1.83 s、負閉路ありのケースが 0.84 s。

Step-by-Step 解説

1不等式を辺にする

「$x_v\le x_u+c$」を辺 $u\to v$(重み $c$)に対応させる。最短路の距離は全辺で $\mathrm{dist}(v)\le\mathrm{dist}(u)+c$ を満たすので、最短距離はそのまま不等式の解 になる。

2最大化が「最短」路になる理由

頂点 1 から $N$ への任意の経路 $1=v_0\to v_1\to\dots\to v_k=N$ に沿って不等式を足すと $x_N-x_1\le(\text{経路の重み})$。すべての経路について成り立つので $x_N-x_1\le\mathrm{dist}(N)$。一方 $x_i=\mathrm{dist}(i)$(届かない頂点は後で述べる)とおけば等号が達成される。よって最大値=最短距離。

3負閉路=矛盾

閉路 $v_0\to\dots\to v_0$ に沿って不等式を足すと $0\le(\text{閉路の重み})$。重みが負なら矛盾で、解が無い。逆に負閉路が無ければ、全頂点を距離 0 から始めた Bellman–Ford の結果が解になる(仮想の始点 $s$ から全頂点へ重み 0 の辺を張ったのと同じ)。

4始点から届かない負閉路の罠

よくある実装は「頂点 1 から Bellman–Ford を回し、$N$ 周目に更新があれば -1」。しかし頂点 1 から届かない場所の負閉路は $\infty$ のまま緩和されず、見逃される。入力例 2 がそれで、-1 が正しいのに -2 を出す。
本解は 1 回目の Bellman–Ford を全頂点 0 で始める ことで、どこにある負閉路も見つける。全探索との照合で、頂点 1 始点だけの版は 300 ケース中 10 ケースを誤った(実測。10 件とも -1 を -2 と答えた)。

5$\infty$=いくらでも大きくできる

負閉路が無く、頂点 1 から $N$ に届かないなら、頂点 1 から届く頂点の集合 $R$ に対して $R$ の外の頂点をまとめて $+t$ ずらしても、$R$ から外へ出る辺が無いので条件は壊れない(外から $R$ へ入る辺 $u\to v$ は $x_v\le x_u+c$ で、$x_u$ が増えるだけなので保たれる)。$t\to\infty$ で $x_N-x_1\to\infty$。

迷った他案と採らなかった理由

案計算量採否理由
頂点 1 始点の Bellman–Ford 1 回$O(NM)$不採用届かない負閉路を見逃す(300 ケース中 10 ケース誤答、実測)。多くの解説がこの形
Floyd–Warshall$O(N^3)$検算用のみ$N=1000$ で $10^9$。$N\le30$ の検算には使った(300 ケース一致)
SPFA(キュー付き Bellman–Ford)平均は速いが最悪 $O(NM)$不採用最悪ケースを作られると遅い点は同じで、負閉路判定に「各頂点がキューに入った回数」の管理が要り、誤りやすい
ポテンシャル付き Union-Findほぼ $O(M)$不採用等式 $x_b-x_a=d$ しか扱えない(Day067 Q3)。不等式には使えない
線形計画ソルバ—不採用差分制約は最短路で厳密に解けるので不要。整数解も自動的に得られる
Bellman–Ford 2 回(本解)$O(NM)$採用全探索 300 ケース・Floyd–Warshall 300 ケースと全件一致

前提が変わったら:

  • $x_N-x_1$ の最小値 を求めるなら、$x_1-x_N\le$ の最短路、すなわち頂点 $N$ から頂点 1 への最短距離 $\mathrm{dist}_N(1)$ を使い、答えは $-\mathrm{dist}_N(1)$
  • 重みが非負だけ(仲の悪い組が無い)なら Dijkstra で $O(M\log N)$ に落とせる。ただし本問は $-d$ の辺があるので使えない
  • 制約が $N=10^5$ 規模 なら $O(NM)$ は重い。負の辺が「並び順」の $0$ 辺と少数の $-d$ 辺だけ、などの構造があれば Johnson 法(ポテンシャルで Dijkstra 化)を検討する

よくあるミス

ミス原因正しい書き方
頂点 1 始点の Bellman–Ford だけで負閉路判定届かない負閉路は $\infty$ のまま1 回目は全頂点 0 で始める(入力例 2。300 中 10 ケース誤答)
仲の悪い組を辺 $a\to b$ 重み $-d$ にする不等式の向きの取り違え$x_a-x_b\le-d$ なので辺 $b\to a$
並び順 $x_i\le x_{i+1}$ を入れ忘れる問題文の「番号順に並ぶ」を読み落とす辺 $i+1\to i$ 重み 0
INF + w で比較して inf 同士の緩和が起きるPython の float('inf') は inf + (-3) = inf で問題ないが、10**18 などを INF にすると負の辺で値が下がるdu != INF を先に確かめる

次のステップ

  • 発展1: 解の 1 つ(各 $x_i$)も出力する。1 回目の Bellman–Ford の距離をそのまま使えば、全条件を満たす配置になる
  • 発展2: 条件が「$x_b-x_a\le d$ または …」のように OR を含むと NP 困難になりうる。2 択ずつなら 2-SAT と組み合わせる型を調べる
  • 次回予告: 焦点帯 M1(DP・ゲーム)を続ける

検証ログ(本セッションで実行した数値)

  • 全探索との一致(実測): $N\le4$、各組 3 個以下、$d\le4$ のランダム 300 ケース(内訳: 解なし 101・無限大 122・有限値 77)で、プログラム全体(標準入出力)の出力が「$x_1=0$、$x_i\in[0,40]$ の全整数配置」の探索と全件一致(最大値が 20 以上なら無限大と判定。有限なら最大値は高々 $3\times4=12$ なので区別できる)
  • Floyd–Warshall との一致(実測): $N\le30$、仲良し 40 個以下・仲の悪い組 40 個以下の 300 ケース(解なし 260・無限大 23・有限値 17)と、仲の悪い組 6 個以下の 300 ケース(解なし 169・無限大 61・有限値 70)で全件一致
  • 頂点 1 始点だけの版(実測): 全探索の 300 ケース中 10 ケースで誤答。入力例 2 はそのうちの 1 つ(-1 が正しいのに -2)
  • 実行時間(実測): $N=1000$、$ML=MD=5000$ で最悪順の鎖 1.83 s(出力 999)、負閉路あり 0.84 s(出力 -1)。$ML=MD=10^4$ にすると最悪 2.72 s だったので制約を 5000 にした
  • 扱っていない範囲: 蟻本・CLRS の原文との照合、SPFA の実装と比較

自己評価

自分の回答

気づき・メモ