AOJ 2386 - Sightseeing Tour
問題
Sightseeing Tour | Aizu Online Judge
問題概要
はじめ、頂点数の完全無向グラフが与えられる。これらの無向辺をすべて、有効辺に置き換えることにした(頂点と頂点の間の辺は→か→のどちらかのみが張られる)。そのときのコストがで与えられる。全ての頂点をちょうど一度づつ通るようなパスが存在するようにこの作業を行うときのコストの最小値を求めよ。
アイデア
解説を見ながら。
まず、このようにグラフ上の全ての頂点を 1 度ずつ通る路のことを"ハミルトンパス"といい、任意の2点が1つの有向辺で結ばれているグラフを"トーナメント"という名前が付いている。今回、最終的に得られるグラフの形はまさにトーナメントである。
そして、「任意のトーナメントグラフにはハミルトンパスが存在する」。つまり、どのように有向辺を張っても題意を満たすような辺の張り方が可能であるということになる。これを頂点数に関する帰納法で示せる。
(証明)
(1) まず、のときは明らか。
(2) 次に、でこれを仮定する。すると、頂点に番号を適当につけることによって→→...→→というようなパスが存在していることになる。そして、頂点と頂点の間には、頂点から出ている辺(out
と呼ぶ)か、頂点に入っていく辺(in
と呼ぶ)のどちらかが存在していることになる。
もし頂点はin
、頂点はout
になっているようなが存在すれば、既存のルートの→の部分を→→というようにすればよい。
逆に、そのようなが存在しない時、以下の3通りに場合分け出来る:
- 全ての頂点が
in
- 全ての頂点が
out
- ある頂点を境界にして、それより前が
out
、後ろがin
(out
,out
, ...,out
,in
, ...,in
のような形)
この時は単純で、の状態がout
なら、→というパスが既存のルートに加わるだけで、の状態がin
なら、→というパスを既存のルートに加えれば良い。そして以上の3つの場合は、少なくとも一方は満たしているので、このようなパスを作ることが出来る。
よって、でも正しい。
(証明終)
これが分かってしまうと後は簡単で、すべての頂点対に対して、との小さい方を選べばよいということになる。
実装(C++)
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define each(itr,c) for(__typeof(c.begin()) itr=c.begin(); itr!=c.end(); ++itr) #define all(x) (x).begin(),(x).end() #define mp make_pair #define pb push_back #define fi first #define se second int main() { int n; cin >>n; int c[100][100]; rep(i,n)rep(j,n) scanf(" %d", &c[i][j]); long ans=0; rep(i,n)rep(j,i) ans+=min(c[i][j],c[j][i]); printf("%ld\n", ans); return 0; }