【問題描述】春天來了,樂樂在某一天的中午做了一個奇怪而又溫馨的夢,以下是夢境的描述:綿中21XX 級信奧班實現了男女人數平均,歐教本著“人人都有朋友耍,人人都有一等拿”的教學原則,準備為機房的每個同學牽紅線。並且由於21XX 年世界男女比例對男生有利,所以只要一個男生喜歡一個女生,他們就可以耍朋友(耶~!)。現給出每個男生喜歡哪些女生(沒錯,是哪些,因為在樂樂的夢裡一個男生喜歡N 個女生都是很符合邏輯的),並給出歐教已經安排好的一種匹配方式。問:在滿足“人人都有朋友耍”的原則下,每個男生可以和哪些女生耍朋友而不會出現讓其他男生無朋友可耍的情況(包括歐教已經安排好的原配)。【輸入資料】第一行一個整數n,表示有n 個男生和n 個女生。接下來n 行每行若干個整數m,a1,a2,…,am。第i 行表示第i 個男生喜歡m 個女生,她們分別是a1,a2,…,am。最後一行共n 個整數,第i 個整數di 表示歐教給出的第i 個男生的原配是di。【輸出資料】共 n 行,每行若干個整數c,b1,b2,…,bc。第i 行表示第i 個男生可以和c 個女生耍朋友,這些女生是b1,b2,…,bc。(女生的編號務必按照從小到大的順序輸出,女孩包含歐教事先給出的原配。)【範例輸入】42 1 22 1 22 3 42 3 41 2 3 4【範例輸出】2 1 22 1 22 3 42 3 4【範例解釋】圖①為一種匹配方式(歐教給出的原配);圖②為滿足原則的另一種匹配。
【資料範圍及約定】1≤n≤2000。由於大家都還是高中生,不會特別花心,所以總的關係數不會超過150000 條。
這道題一看到很像二分圖匹配。
但是看到題目,總是會感覺匈牙利演算法怎麼用都用不上。
於是,我們沿著匈牙利演算法的思路來繼續想這道題。
首先,匈牙利演算法從左半集出發,通過一條匹配邊、一條非匹配邊、一條匹配邊……最終到達右半集,這時匹配數+1。
能不能用類似這樣的方法但讓匹配數不加一呢?(也就是換一個匹配。)
當然可以!直接構成一個環就行了。
於是,將所有原配建成從右至左的邊,把其餘的建成從左至右的邊,這樣就可以用Tarjan找出強連通分量,最後在所有的強連通分量中找出其中含有的邊即可,即為所有可行的匹配。
Accode:
#include <cstdio>#include <cstdlib>#include <algorithm>#include <string>#define min(a, b) ((a) < (b) ? (a) : (b))const int maxN = 2010;struct Edge {int v; Edge *next;};Edge *edge[maxN << 1];bool marked[maxN << 1], mp[maxN][maxN];int DFN[maxN << 1], Low[maxN << 1];int stack[maxN << 1], Belong[maxN << 1];int Link[maxN], n, Bcnt, Ind, top;inline int getint(){ int res = 0; char tmp; while (!isdigit(tmp = getchar())); do res = (res << 3) + (res << 1) + tmp - '0'; while (isdigit(tmp = getchar())); return res;}inline void Ins(int u, int v){ Edge *p = new Edge; p -> v = v; p -> next = edge[u]; edge[u] = p; return;}void Tarjan(int u){ DFN[u] = Low[u] = ++Ind; marked[stack[++top] = u] = 1; for (Edge *p = edge[u]; p; p = p -> next) { if (!DFN[p -> v]) { Tarjan(p -> v); Low[u] = min(Low[u], Low[p -> v]); } else if (marked[p -> v]) Low[u] = min(Low[u], DFN[p -> v]); } if (DFN[u] == Low[u]) { int tmp = u; ++Bcnt; do { tmp = stack[top--]; marked[tmp] = 0; Belong[tmp] = Bcnt; } while (tmp != u); } return;}int main(){ freopen("love.in", "r", stdin); freopen("love.out", "w", stdout); n = getint(); for (int i = 1; i < n + 1; ++i) for (int cnt = getint(); cnt; --cnt) mp[i][getint()] = 1; for (int i = 1; i < n + 1; ++i) { int j = getint(); Link[j] = i; Ins(j + n, i); } for (int i = 1; i < n + 1; ++i) for (int j = 1; j < n + 1; ++j) if (mp[i][j] && Link[j] != i) Ins(i, j + n); for (int i = 1; i < n + 1; ++i) if (DFN[i]) Tarjan(i); for (int i = 1; i < n + 1; ++i) { int cnt = 0; for (int j = 1; j < n + 1; ++j) if (mp[i][j] && Belong[i] == Belong[j + n]) ++cnt; printf("%d", cnt); for (int j = 1; j < n + 1; ++j) if (mp[i][j] && Belong[i] == Belong[j + n]) // printf(" %d", j); printf("\n"); } return 0;}#undef min