题目大意:给出一个二分图的完美匹配(王子和公主的烧死名单表),二分图x部和y部均只有n个点,问对于每一个x部的点,他能选择哪些点与之匹配 使得与之匹配后,剩余图的最大匹配仍然是n
思路:这题是大白书379页二分图的压轴题,在图论刷的题还不多时思考过这题,现在想来也不难想
这题引人瞩目的一点便是预先给出了一个二分图的初始匹配
对每个点枚举后增广显然不怎么可行,那么还是图论问题的经典思考方式,点和边各表示什么
题目的输入天然的给出了一个图,但对这题好像没什么用处,于是开始思考把给出的初始匹配的每条边看成一个点!!!
那样构成的图每个点就是一个王子和一个公主的配对,如果一个王子还可以花心爱上除了初始匹配的公主外的其他公主,那么从这个点向那个公主处连一条边
然后显然新构成的图中如果有环,(A到B,B又可以到A),那么我们就可以把原来二分图中的匹配顺次沿一格(形象一点就是环中的王子们顺次把自己初始匹配的姑娘送给下一个王子),这样肯定不改变二分图的最大匹配数
然后联想到在一个强连通分量里面,任意两点间肯定是有环的,因此可以缩点,一个SCC里的姑娘肯定可以取到的,不在一个SCC里的姑娘由于是DAG 所以不可能存在环,因此无论如何也取不到
然后就A了,输出的时候得递增 因此贡献了发WA
#include<cstdio>
#include<string.h>
#include<iostream>
#include<algorithm>
#define maxn 600090
using namespace std;
int head[maxn],next[maxn],point[maxn],now=0;
int stack[maxn],top,col,dfn[maxn],low[maxn];
int tim,belong[maxn],x[maxn],y[maxn];
int match[maxn],ans[maxn],h=0,idx=0;
bool instack[maxn];
void add(int x,int y)
{
next[++now]=head[x];
head[x]=now;
point[now]=y;
}
void tarjan(int k)
{
stack[++top]=k;
dfn[k]=low[k]=++tim;
instack[k]=1;
for(int i=head[k];i;i=next[i])
{
int u=point[i];
if(dfn[u]==0)
{
tarjan(u);
low[k]=min(low[k],low[u]);
}
else if(instack[u]==1)
{
low[k]=min(low[k],low[u]);
}
}
if(dfn[k]==low[k])
{
col++;
int u;
do
{
u=stack[top--];
belong[u]=col;
instack[u]=0;
}while(u!=k);
}
}
int main()
{
int n,k,tt;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&k);
for(int j=1;j<=k;j++)
{
scanf("%d",&tt);
x[++h]=i;y[h]=tt;
}
}
for(int i=1;i<=n;i++)
{
scanf("%d",&tt);
match[tt]=i;
}
for(int i=1;i<=h;i++)
{
if(match[y[i]]!=x[i])
{
add(x[i],match[y[i]]);
}
}
for(int i=1;i<=n;i++)if(dfn[i]==0)tarjan(i);
for(int i=1;i<=n;i++)
{
int hh=0;
while(x[idx+1]==i && idx+1<=h)
{
idx++;
if(belong[i]==belong[match[y[idx]]])
ans[++hh]=y[idx];
}
printf("%d",hh);
sort(ans+1,ans+1+hh);
for(int i=1;i<=hh;i++)
{
printf(" %d",ans[i]);
}
printf("\n");
}
return 0;
}
POJ 1904:King's Quest【tarjan】