等效集合 图论(缩点)
[题目描述]
要使两个集合 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; }
PEACE