1. 程式人生 > >HDU ~ 4685 ~ Prince and Princess (二分圖匹配(網路流) + 強連通縮點)

HDU ~ 4685 ~ Prince and Princess (二分圖匹配(網路流) + 強連通縮點)

在這裡插入圖片描述

題意

T組測試資料,每組先輸入n,m表示有n個王子和m個公主,接下來n行,每行先輸入k表示,第i個王子有K個喜歡的公主,然後輸入這k個公主的編號。每個王子只能和喜歡的妹子結婚,國王要求給他一個表,每個王子可以和幾個公主結婚,按序號升序輸出妹子的編號。這個表應滿足所有的王子最終都有妹子和他結婚,即必須保證王子與這些物件中的任意一個結婚,都不會影響讓剩餘的王子中突然有一個人沒婚可結了。

思路

其實基本和POJ ~ 1904 ~ King’s Quest (強連通 + 縮點,思維)一樣,只不過這個題需要我們求出一種完美匹配。
跑一次n,m個點的二分圖匹配以後,此時可能不是完美匹配,即可能會有公子和公主沒人結婚,所以我們需要想辦法給這些人安排一個物件。
怎麼辦?那就是虛擬一些公子和公主。
假設第一次二分圖匹配的最大匹配數為res,那麼左邊就有n-res個人沒物件,右邊m-res個沒物件。
那麼左邊需要新增m-res個虛擬點,右邊需要新增n-res個虛擬點。這樣左右兩邊都有N=n+m-res個點。
此時再求一次N,N個點的二分圖匹配,即可得到一種完美匹配。
強連通圖中共有N*2個點,建邊有:
①輸入中的王子喜歡公主的邊
②虛擬王子和所有公主之間的邊,所有王子和虛擬公主之間的邊
③根據匹配建邊,如果u喜歡v,那麼就建v->u的邊

強連通縮點,在同一個強連通分量中的點可以隨意結婚。

二分圖匹配我是拿網路流做法來寫的。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+5;

const int INF = 0x3f3f3f3f;
struct EDGE
{
    int from, to, cap, flow;       //起點,終點,容量,流量
    EDGE(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};
struct Dinic
{
int n, m, s, t; //結點數,邊數(包括反向弧),源點s,匯點t vector<EDGE> edges; //邊表。edges[e]和edges[e^1]互為反向弧 vector<int> G[MAXN]; //鄰接表,G[i][j]表示結點i的第j條邊在edges陣列中的序號 int d[MAXN]; //從起點到i的距離(層數差) int cur[MAXN]; //當前弧下標 bool
vis[MAXN]; //BFS分層使用 void init(int n) { this->n = n; edges.clear(); for (int i = 0; i <= n; i++) G[i].clear(); } void AddEdge(int from, int to, int cap) { edges.push_back(EDGE(from, to, cap, 0)); edges.push_back(EDGE(to, from, 0, 0)); m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); } bool BFS()//構造分層網路 { memset(vis, 0, sizeof(vis)); queue<int> Q; d[s] = 0; vis[s] = true; Q.push(s); while (!Q.empty()) { int x = Q.front(); Q.pop(); for (int i = 0; i < G[x].size(); i++) { EDGE& e = edges[G[x][i]]; if (!vis[e.to] && e.cap > e.flow) { vis[e.to] = true; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a)//沿阻塞流增廣 { if (x == t || a == 0) return a; int flow = 0, f; for (int& i = cur[x]; i < G[x].size(); i++)//從上次考慮的弧 { EDGE& e = edges[G[x][i]]; if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0)//多路增廣 { e.flow += f; edges[G[x][i]^1].flow -= f; flow += f; a -= f; if (a == 0) break; } } return flow; } int MaxFlow(int s, int t) { this->s = s; this->t = t; int flow = 0; while (BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, INF); } return flow; } }solve; struct Edge { int from, to; Edge (int from, int to): from(from), to(to) {} }; struct SCC { int n, m; int DFN[MAXN], LOW[MAXN], sccno[MAXN], dfs_clock, scc_cnt; vector<Edge> edges; vector<int> G[MAXN]; stack<int> S; void init(int n) { this->n = n, m = 0; edges.clear(); for (int i = 0; i <= n; i++) G[i].clear(); } void AddEdge (int from, int to) { edges.push_back(Edge(from, to)); m = edges.size(); G[from].push_back(m-1); } void dfs(int u) { DFN[u] = LOW[u] = ++dfs_clock; S.push(u); for (int i = 0; i < G[u].size(); i++) { Edge e = edges[G[u][i]]; int v = e.to; if (!DFN[v]) { dfs(v); LOW[u] = min(LOW[u], LOW[v]); } else if (!sccno[v]) LOW[u] = min(LOW[u], DFN[v]); } if (LOW[u] == DFN[u]) { scc_cnt++; while (1) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if (x == u) break; } } } void find_scc() { dfs_clock = scc_cnt = 0; memset(DFN, 0, sizeof(DFN)), memset(sccno, 0, sizeof(sccno)); for (int i = 0; i < n; i++) if (!DFN[i]) dfs(i); } }gao; int n, m, N; vector<Edge> E; void build() { int s = n+m, t = n+m+1; solve.init(t); for (auto i: E) solve.AddEdge(i.from, i.to+n, 1); for (int i = 0; i < n; i++) solve.AddEdge(s, i, 1); for (int i = 0; i < m; i++) solve.AddEdge(i+n, t, 1); int res = solve.MaxFlow(s, t);//第一次二分圖匹配 N = n+m-res; s = N*2, t = N*2+1; solve.init(t); gao.init(N*2);//強連通圖中有N*2個點 for (auto i: E) solve.AddEdge(i.from, i.to+N, 1); for (int i = 0; i < N; i++) solve.AddEdge(s, i, 1); for (int i = 0; i < N; i++) solve.AddEdge(i+N, t, 1); for (int i = n; i < N; i++) for (int j = N; j < N*2; j++) solve.AddEdge(i, j, 1), gao.AddEdge(i, j);//虛擬王子喜歡所有公主 for (int i = 0; i < N; i++) for (int j = N+m; j < N*2; j++) solve.AddEdge(i, j, 1), gao.AddEdge(i, j);//所有王子喜歡虛擬公主 res = solve.MaxFlow(s, t);//第二次二分圖匹配 for (auto i: E) gao.AddEdge(i.from, i.to+N);//輸入中的邊加入強連通 for (auto i: solve.edges) if (i.flow == 1 && i.from != s && i.to != t) gao.AddEdge(i.to, i.from);//匹配邊,公主向王子建邊 } int main() { int T, CASE = 1; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); E.clear(); for (int u = 0; u < n; u++) { int k; scanf("%d", &k); while (k--) { int v; scanf("%d", &v); v--; E.push_back(Edge(u, v)); } } build(); gao.find_scc(); printf("Case #%d:\n", CASE++); for (int u = 0; u < n; u++) { vector<int> ans; for (auto i: gao.G[u]) { int u = gao.edges[i].from, v = gao.edges[i].to; if (gao.sccno[u] == gao.sccno[v]) if (v-N+1 <= m) ans.push_back(v-N+1); } sort(ans.begin(), ans.end()); printf("%d", ans.size()); for (int i = 0; i < ans.size(); i++) printf(" %d", ans[i]); printf("\n"); } } return 0; } /* 2 4 4 2 1 2 2 1 2 2 2 3 2 3 4 1 2 2 1 2 */