Communication 并查集+弗洛伊德 或者 强连通分量

一开始以为是并查集,以为只有  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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值