http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2230
LP双対の問題。似たような問題でLongest Shortest Pathがあるのでそれの解説スライドを見ながら考察した。
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2736
まず最長距離の双対を取ってポテンシャルに言い換える。
すると条件は
max (Σx)
s.t.
d[t]-d[s]<=D (双対変数F)
d[u]-d[v]+xe<=-le (for all e=(u,v)) (双対変数fe)
となる。(新しくdという変数を追加するのがポイント)
これの双対をとると
min Σ(-ce*fe)+D*F
(sから出る辺のfeの合計)>=F ...(1)
(vから出る辺のfeの合計)-(uに入る辺のfeの合計)>=0 ...(2)
(tに入る辺のfeの合計)<=F ...(3)
fe >= 1 ...(4)
(2)について考えると、各点で水が足される可能性があるとみてよい。
なのでF'(>=F)だけsから流されたとすると、tはF''(>=F')だけ水を受け取ることになる。
しかし、(3)の条件よりF''=F'=Fとなり、
(1)(2)(3)<=>
(sから出る辺のfeの合計)=F
(vから出る辺のfeの合計)-(uに入る辺のfeの合計)=0
(tに入る辺のfeの合計)=Fとなり、これは流量条件である。
(4)と合わせてこの問題は、
各辺1の最小流量制約があるグラフにおいて、M=(Fだけ水を流したときのコスト)+D*Fを最小化する問題に言い換えられた。
Fを固定すれば
(Fだけ水を流したときのコスト)については最小費用流で最小化すればよい。
また流量FのフローはF本のsからtへのpathに分解できることに留意すると、
流量をさらに1増やしたとき新しくpathが追加されるが、その長さはDより小さい。
よってFを増やすとMは必ず大きくなるので、F=(すべての辺をpathでカバーする際の本数の最小値)となる。これはにぶたんで求めてもいいし、sとtに辺を張って一発で求めることもできる。
にぶたんのupper_boundをMではなくNにしてしまいこれのせいで時間を無限に溶かした。過去の自分のブログを見返したら同じミスをしていて笑った。
また上の式を見てもらえばわかるが、最小費用流の双対は
d[u]-d[v]+xe<=leのような形をしていることがわかる。
http://tokoharuland.hateblo.jp/entry/2016/12/06/223614
を見て貰えばわかるが、最大流量の制約も追加すると式はd[u]-d[v]+xe-ye<=leのようになる。3or4変数の式がたくさん出てきたら最小費用流の双対を疑うべき。
下はsからtへ辺を張る方法でFを求めている。
int N, M, F; int A[MAX_N], B[MAX_N], C[MAX_N]; int S[MAX_N]; void solve() { cin >> N >> M; MCF::init(N); rep(i, 0, M) { cin >> A[i] >> B[i] >> C[i]; MCF::add_edge(A[i], B[i], inf, 0); MCF::add_edge(A[i], B[i], 1, -2); } MCF::add_edge(0, N - 1, inf, -1); int f = MCF::get(0, N - 1, M) + 3 * M; int res = 0; int s = N, t = N + 1; MCF::init(N + 2); S[0] = -f; S[N - 1] = f; rep(i, 0, M) { MCF::add_edge(A[i], B[i], inf, -C[i]); S[A[i]]++; S[B[i]]--; res += -C[i]; } int d = MCF::get_longest(0, N - 1); F = 0; rep(i, 0, N) { if(S[i] > 0) MCF::add_edge(i, t, S[i], 0); if(S[i] < 0) { MCF::add_edge(s, i, -S[i], 0); F += -S[i]; } } res += MCF::get(s, t, F); cout << res + d * f << "\n"; }
高難易度の問題はWA出すと本質が間違っているのではないかと疑いがち。自信を持ってください。