AOJ 1615 Gift Exchange Party
解法
友人の助けを借りて解いた.
この問題は最大流の問題に帰着させることができる.(想定解は違う方法だった.)
まず,二部グラフ {V1, V2} をつくる.V1 は n 頂点からなり,各人を表す.V2 は m 頂点からなり,親しい友人関係を表す.
親しい友人関係 i が u, v (V1 の頂点) であるならば, u -> 関係 i, v -> 関係 i にそれぞれ容量 1 の辺をはる.
次に,source -> 各人 に容量をいい感じに定めて辺をはる(これはあとで説明する).
最後に,各関係 -> dest に容量 1 の辺をはる.
こうしでできたグラフに対し,source から dest にフローを流す.この時,source -> 各人 の辺に流れている流量が,その人がもらえるプレゼントを表していることがわかる.
あとは,source -> 各人の容量をいい感じに定めていく.
プレゼントを一番多くもらえる人のプレゼントの数の最小値 x は,source ->
各人の辺の容量を小さい方から試していって,初めて m の流量が流れた(つまりプレゼントの受け渡しが完全に終わった)ときの容量に対応していることがわかる.
また,プレゼントを一番もらえない人のプレゼントの数の最大値 y は,source -> 各人の容量を c と定めた時,c * n の流量が流れるような c の最大値に対応することがわかる.
これは,最適な解の時のプレゼントの受け渡し方をフロー上で考えるとわかる.プレゼントを一番もらえない人のプレゼント数を k とすると,それ以外の人は必ず k 以上もらっている.つまり,source -> 各人の容量を k に絞ると,流量は n * k になるはずである.
さらに,このようにフローを流すと,その時の最大値は,先ほど求めた x に対応していることが明らかにわかる(平均的に流せるなら当然そうすべきなので).
n が小さいので割りと適当に書いても通る.
ソースコード
#include <bits/stdc++.h> using namespace std; struct edge { int to, cap, rev; }; using edges = std::vector<edge>; using graph = std::vector<edges>; void add_edge(graph& g, int from, int to, int cap) { g[from].push_back(edge{to, cap, static_cast<int>(g[to].size())}); g[to].push_back(edge{from, 0, static_cast<int>(g[from].size()-1)}); } int dfs(graph& g, std::vector<bool>& used, int v, int t, int f) { if(v == t) { return f; } used[v] = true; for(int i=0; i<g[v].size(); ++i) { edge& e = g[v][i]; if(!used[e.to] && e.cap > 0) { int d = dfs(g, used, e.to, t, std::min(f, e.cap)); if(d > 0) { e.cap -= d; g[e.to][e.rev].cap += d; return d; } } } return 0; } int max_flow(graph& g, int s, int t) { int flow = 0; int INF = 1e9; std::vector<bool> used(g.size(), false); while(true) { std::fill(used.begin(), used.end(), false); int f = dfs(g, used, s, t, INF); if(f == 0) { return flow; } flow += f; } } int calc_max(int n, vector<int> const& u, vector<int> const& v) { const int m = u.size(); graph g(n + m + 2); const int source = n + m; const int dest = n + m + 1; for(int i=0; i<m; ++i) { add_edge(g, u[i], n + i, 1); add_edge(g, v[i], n + i, 1); add_edge(g, n + i, dest, 1); } for(int i=0; i<n; ++i) { add_edge(g, source, i, 0); } int flow = 0; int res = 1; for(; ; ++res) { for(auto& e : g[source]) { e.cap += 1; } flow += max_flow(g, source, dest); if(flow == m) { break; } } return res; } int calc_min(int n, vector<int> const& u, vector<int> const& v) { const int m = u.size(); graph g(n + m + 2); const int source = n + m; const int dest = n + m + 1; for(int i=0; i<m; ++i) { add_edge(g, u[i], n + i, 1); add_edge(g, v[i], n + i, 1); add_edge(g, n + i, dest, 1); } for(int i=0; i<n; ++i) { add_edge(g, source, i, 0); } int res = 0; int flow = 0; for(int i = 1; i <= 100; ++i) { for(auto& e : g[source]) { e.cap += 1; } flow += max_flow(g, source, dest); if(flow == n * i) { res = i; } } return res; } int main() { int n, m; while(cin >> n >> m, n) { vector<int> u(m), v(m); for(int i=0; i<m; ++i) { cin >> u[i] >> v[i]; u[i]--; v[i]--; } int ma = calc_max(n, u, v); int mi = calc_min(n, u, v); cout << mi << ' ' << ma << endl; } }