[题目描述]
要使两个集合 A,B 是等效的,我们可以先让 A 成为 B 的子集,然后再让 B 成为 A 的子集,
这样就完成了。
使用上面的方法,我们要让 N 个集合都等效:每一步,你可以让集合 X 成为 Y 的子集。注
意,有一些集合是已经是其他集合的子集了。求操作最少需要经过多少步?
[输入数据]
输入包含多组测试数据,每组数据的第一行是两个整数 N,M,接下来 M 行,每行两个数 X,
Y,表示集合 X 已经是 Y 集合的子集。
[输出数据]
对于每组测试数据,输出一行,一个数,表示最少要经过的步数
[输入样例]
4 0
3 2
1 2
1 3
[输出样例]
4
2
[数据范围]
对于 50%的数据, N <= 2000 and M <= 5000
对于 100%的数据,N <= 20000 and M <= 50000
这题的题意一开始我没怎么理解,问了别人后知道这其实是一道图论题。
首先我们看到它要求的全部等效,其实就是求一张强联通图,而这题就是问我们给出的图加几条边可以变成一张强联通图。
于是就需要用到Tarjan缩点的方法。
说到Tarjan缩点,我一直也不是很精通,只是了解原理,代码几乎没怎么打过,这道题也就当打模板了。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
#define il inline
#define db double
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
il int gi()
{
int x=0,y=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
y=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*y;
}
int head[50045],cnt;
struct edge
{
int to,next;
}e[50045];
il void add(int from,int to)
{
e[++cnt].next=head[from];
e[cnt].to=to;
head[from]=cnt;
}
int low[50045],dfn[50045],in[50045],out[50045];//low记录该点可以往上回溯最远的深度,dfn记录该点深度
int stack[50045],belong[50045];
int n,m,top,coun,num;
bool vis[1000045];
void Tarjan(int x)
{
dfn[x]=low[x]=++num;
stack[top++]=x;//入栈
vis[x]=1;//标记在栈中
int r=head[x];
while(r!=-1)
{
int now=e[r].to;
if(!dfn[now])//没被遍历过
{
Tarjan(now);
low[x]=min(low[x],low[now]);//low取最小值
}
else
if(vis[now])//已经在栈里面了
low[x]=min(low[x],dfn[now]);
r=e[r].next;
}
if(low[x]==dfn[x])//该点可以缩
{
coun++;
int tmp;
while(1)
{
tmp=stack[--top];//把可以缩的缩了
belong[tmp]=coun;//记录该点缩了后在哪个点
vis[tmp]=0;//出栈
if(tmp==x)
break;
}
}
}
int main()
{
freopen("set.in","r",stdin);
freopen("set.out","w",stdout);
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,-1,sizeof(head));
memset(belong,0,sizeof(belong));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(stack,0,sizeof(stack));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
cnt=0;
coun=0;
num=0;
int x,y;
for(int i=1;i<=m;i++)
{
x=gi(),y=gi();
add(x,y);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
Tarjan(i);
int rudu=0,chudu=0;
for(int i=1;i<=n;i++)
{
int t=belong[i];
int r=head[i];
while(r!=-1)
{
int now=e[r].to;
if(belong[now]!=t)
{
in[belong[now]]++;
out[t]++;
}
r=e[r].next;
}
}
for(int i=1;i<=coun;i++)
{
if(in[i]==0)
rudu++;
if(out[i]==0)
chudu++;
}
printf("%d\n",max(rudu,chudu));
}
return 0;
}