题目链接:http://poj.org/problem?id=1904
题意:国王有n个儿子,每个儿子喜欢ki个女孩,国王想让王子与他喜欢的人结婚,就让巫师做一个列表出来,但是国王想知道王子能和哪些女孩结婚,并且不影响其他王子也能与自己喜欢的女孩在一起;
其实就是求王子能和哪些女孩结婚,不影响最大匹配;
假如已知匹配王子属于x,女孩属于y
现有匹配xi---yi xj---yj 并且xi也喜欢yj
如果说xi能和yj匹配并不改变匹配总数,必须满足一下两个条件之一:
1:xj也喜欢yi;
2:yi 被某个匹配中的 xk喜欢,并且 xi 也喜欢 yk ;
思路很像匈牙利算法中找曾广路的过程;xi--->yj--->xk--->yi--->xi(是一个环,各点都能相互到达,所以其中的匹配可以互换,并且不影响匹配总数);
建图:王子u喜欢女孩v,则u到v连一条边。对于给出的初始完美匹配,王子u与女孩v匹配,则v到u连一条边。然后求SCC。
显然对于同一个SCC中王子数目和女孩数目是相等的,并且从某个王子出发能够到达所有女孩,这样,王子可以和属于同一个SCC中的任意一个女孩结婚,而不会影响其他王子。
#include<stdio.h> #include<string.h> #include<vector> #include<algorithm> using namespace std; #define met(a, b) memset(a, b, sizeof(a)) const int N = 4050; int n, low[N], dfn[N], Time; int IsSta[N], Sta[N], top, Belong[N], cnt, ans[N]; vector<vector<int> >G; void Init() { met(low, 0); met(dfn, 0); met(IsSta, 0); met(Sta, 0); met(Belong, 0); G.clear(); G.resize(N+1); Time = cnt = top = 0; } void Tarjan(int u) { low[u] = dfn[u] = ++ Time; IsSta[u] = 1; Sta[top++] = u; int len = G[u].size(), v; for(int i=0; i<len; i++) { v = G[u][i]; if(!dfn[v]) { Tarjan(v); low[u] = min(low[u], low[v]); } else if(IsSta[v]) low[u] = min(low[u], dfn[v]); } if(low[u] == dfn[u]) { ++ cnt; do { v = Sta[--top]; IsSta[v] = 0; Belong[v] = cnt; }while(u != v); } } int main() { while(scanf("%d", &n) != EOF) { Init(); int k, x; for(int i=1; i<=n; i++) { scanf("%d", &k); for(int j=1; j<=k; j++) { scanf("%d", &x); G[i].push_back(x+n);///王子编号为1---n,公主编号为n+1---2*n; } } for(int i=1; i<=n; i++) { scanf("%d", &x); G[x+n].push_back(i);///添加反向边; } for(int i=1; i<=n; i++) { if(!dfn[i]) Tarjan(i); } for(int i=1; i<=n; i++) { int len = G[i].size(), u = Belong[i], v; met(ans, 0); int K = 0; for(int j=0; j<len; j++) { v = Belong[ G[i][j] ]; if( u == v )///当他们在一个块中时是可以与之匹配的; ans[K++] = G[i][j]; } sort(ans, ans+K); printf("%d", K); for(int j=0; j<K; j++) printf(" %d", ans[j]-n);///恢复原编号; printf(" "); } } return 0; }