(二分图匹配的可行边)Prince and Princess

王子照样向妹子连边,但是如果某个妹子匹配了王子,把那个妹子向王子连边,然后跑Tarjan,在一个强连通分量里的王子可以任意匹配妹子。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 5, maxm = maxn * maxn;
int g[maxn][maxn], vis[maxn], link[maxn];
bool dfs(int u, int m) {
for(int v = 1; v <= m; ++v) {
if(!vis[v] && g[u][v]) {
vis[v] = 1;
if(!link[v] || dfs(link[v], m)) {
link[v] = u;
return true;
}
}
}
return false;
}
//匈牙利算法
int Hungary(int n, int m) {
int res = 0;
memset(link, 0, sizeof(link));
for(int i = 1; i <= n; ++i) {
memset(vis, 0, sizeof(vis));
if(dfs(i, m)) res++;
}
return res;
}
int head[maxn], tot;
struct Edge {
int to, nxt;
Edge() {}
Edge(int _to, int _nxt) : to(_to), nxt(_nxt) {}
} edge[maxm];
void add(int u, int v) {
edge[tot] = Edge(v, head[u]);
head[u] = tot++;
}
int dfn[maxn], low[maxn], idx, instack[maxn], Stack[maxn], top;
int belong[maxn], block;
void Tarjan(int u) {
dfn[u] = low[u] = ++idx;
Stack[top++] = u;
instack[u] = 1;
for(int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].to;
if(!dfn[v]) {
Tarjan(v);
if(low[u] > low[v]) low[u] = low[v];
}
else if(instack[v] && low[u] > dfn[v])
low[u] = dfn[v];
}
if(dfn[u] == low[u]) {
block++;
int v;
do {
v = Stack[–top];
instack[v] = 0;
belong[v] = block;
} while(u != v);
}
}
void init() {
tot = idx = top = block = 0;
memset(dfn, 0, sizeof(dfn));
memset(instack, 0, sizeof(instack));
memset(g, 0, sizeof(g));
memset(head, -1, sizeof(head));
}
int main()
{
int T;
scanf("%d", &T);
for(int cas = 1; cas <= T; ++cas) {
init();
int n, m;
scanf("%d%d", &n, &m);
for(int u = 1; u <= n; ++u) {
int cnt, v;
scanf("%d", &cnt);
for(int i = 0; i < cnt; ++i) {
scanf("%d", &v);
g[u][v] = 1;
}
}
//res为匹配数
int res = Hungary(n, m);
printf(“初始匹配数:%d\n”, res);
int nn = n + m - res;
//n-r王子与所有公主连接
for(int i = n + 1; i <= nn; ++i)
for(int j = 1; j <= nn; ++j)
g[i][j] = 1;
//m-r公主与所有王子连接
for(int i = 1; i <= n; ++i)
for(int j = m + 1; j <= nn; ++j)
g[i][j] = true;
//跑出完备图
Hungary(nn, nn);
//成图
for(int i = 1; i <= nn; ++i)
if(link[i]) add(i + nn, link[i]);
for(int i = 1; i <= nn; ++i)
for(int j = 1; j <= nn; ++j)
if(g[i][j]) add(i, j + nn);
for(int i = 1; i <= (nn << 1); ++i)
if(!dfn[i]) idx = top = 0, Tarjan(i);
printf(“Case #%d:\n”, cas);
for(int i = 1; i <= n; ++i) {
top = 0;
for(int j = 1; j <= m; ++j) if(g[i][j] && belong[j + nn] == belong[i]) Stack[top++] = j;
printf("%d", top);
for(int j = 0; j < top; ++j) printf(" %d", Stack[j]);
puts("");
}
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值