一开始以为是并查集,以为只有 1 到2 2 到1 这样的才是一个集合 ,忘记了 可以成环,,
1 到 2 ,2 到3 ,3 到1 这样并查集做不了, 于是晨哥上来一个 弗洛伊德 妙手回春。。。开心到不行,,当然强连通分量要马上学啊,,听说tarjan 使用频率挺高的
先说 并查集 + 弗洛伊德,,首先建图,把所有边读入,,在弗洛伊德,,1到2 ,2 到3 题目里面没有说 1到3 但应该我们做出1到3 这一步就是弗洛伊德建图,剩下的交给 并查集
#include <iostream>
#include <queue>
#include <cstring>
#include <string>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=2000005;
int f[maxn];
int b[maxn];
typedef struct
{
int a,b;
}D;
bool vis[205][205];
D a[maxn];
int findd(int a)
{
if(a==f[a]) return a;
else
return f[a]=findd(f[a]);
}
void marge(int a,int b){
int A=findd(a);
int B=findd(b);
if(A!=B)
f[A]=B;
}
int main()
{
int t; cin>>t;
while(t--){
memset(vis,0,sizeof vis);
int n; cin>>n;
int dui; cin>>dui;
for(int i=0;i<n;i++)
f[i]=i;
for(int i=0;i<dui;i++)
{
cin>>a[i].a>>a[i].b;
vis[a[i].a][a[i].b]=1;
}
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(vis[i][k]==1&& vis[k][j]==1)
vis[i][j]=1;
}
for(int i=0;i<dui;i++)
{
if(vis[a[i].a][a[i].b]==1 && vis[a[i].b][a[i].a]==1 )
marge(a[i].a,a[i].b);
}
int cou=0;
for(int i=0;i<n;i++)
if(f[i]==i)
cou++;
cout<<cou<<endl;
}
return 0;
}
还有一种是强连通分量的做法
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
struct node {
int v,next;
}edge[10001];
int DFN[205],LOW[205];
int stackk[205],heads[205],visit[205],cnt,tot,indexx;
int ans=0;
void add(int x,int y) // 链式前向星
{
edge[++cnt].next=heads[x];
edge[cnt].v = y;
heads[x]=cnt;
return ;
}
void tarjan(int x)//代表第几个点在处理。递归的是点。
{
DFN[x]=LOW[x]=++tot;// 新进点的初始化。
stackk[++indexx]=x;//进站
visit[x]=1;//表示在栈里
for(int i=heads[x];i!=-1;i=edge[i].next)
{
if(!DFN[edge[i].v]) {//如果没访问过
tarjan(edge[i].v);//往下进行延伸,开始递归
LOW[x]=min(LOW[x],LOW[edge[i].v]);//递归出来,比较谁是谁的儿子/父亲,就是树的对应关系,涉及到强连通分量子树最小根的事情。
}
else if(visit[edge[i].v ]){ //如果访问过,并且还在栈里。 还在栈里就是 指回去了,
LOW[x]=min(LOW[x],DFN[edge[i].v]);//比较谁是谁的儿子/父亲。就是链接对应关系
}
}
if(LOW[x]==DFN[x]) //发现是整个强连通分量子树里的最小根。
{
do{
// printf("%d ",stack[indexx]);
visit[stackk[indexx]]=0;
indexx--;
}while(x!=stackk[indexx+1]);//出栈,并且输出。
// printf("\n");
ans++;
}
return ;
}
int main()
{
int t; scanf("%d",&t);
while(t--){
ans=0;
cnt=0;
tot=0;
indexx=0;
memset(heads,-1,sizeof(heads));
memset(visit,0,sizeof visit);
memset(DFN,0,sizeof DFN);
memset(LOW,0,sizeof LOW);
int n,m;
scanf("%d%d",&n,&m);
int x,y;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
}
for(int i=0;i<n;i++)
if(!DFN[i]) {tarjan(i);}//当这个点没有访问过,就从此点开始。防止图没走完
printf("%d\n",ans);
}
return 0;
}