等效集合 图论(缩点)

[题目描述]
要使两个集合 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;
}

 

posted @ 2017-10-12 16:13  GSHDYJZ  阅读(244)  评论(0编辑  收藏  举报