tarjan算法(求强连通子块,缩点)

本文深入讲解了Tarjan算法用于寻找图中的强连通子图,并提供了详细的代码实现及实例解析。通过对Tarjan算法核心思想的理解,读者可以掌握如何求解强连通子图的数量,并应用于特定问题场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tarjan算法求图中的强连通子图的个数。

#include<iostream>

#include<stack>
#include<queue>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
# define maxn 100005
using namespace std;
vector<int>wakaka[maxn];
stack<int>q;
int  low[maxn];
int dfn[maxn];
int vis[maxn];
int num,ans;
void tarjan(int u)//u始终代表父亲节点
{
    low[u]=dfn[u]=++num;
    q.push(u);
    vis[u]=1;
    int len=wakaka[u].size();
    for(int i=0; i<len; i++)
    {
        int v=wakaka[u][i];
        if(vis[v]==0)
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        if(vis[v])
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        ans++;
        int top;
        do
        {
            top=q.top();
            q.pop();
            vis[top]=-1;
        }
        while(u!=top);
    }
}
int main()
{
    int n,m;
    while(cin>>n>>m&&(n+m))
    {
        for(int i=1; i<=n; i++)
        {
            wakaka[i].clear();
        }
        ans=num=0;
        while(!q.empty())q.pop();
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=m; i++)
        {
            int u,v;
            cin>>u>>v;
            wakaka[u].push_back(v);
        }
        for(int i=1; i<=n; i++)
        {
            if(vis[i]==0)
            {
                if(ans>=2){
                  break;
                  }
                      tarjan(i);
            }
        }
        if(ans==1)
        cout<<"Yes"<<endl;
        else {
        cout<<"No"<<endl;
        }
        }
    return 0;
}

tarjan算法缩点运算的使用具体事例

题目链接:http://poj.org/problem?id=2186

具体大意:假设有三头公牛a,b,c。a仰慕b,b仰慕c,那么这个c就是剩下的所有公牛的仰慕对象,然后这个题就是让你算出符合条件的公牛一共有多少头。

具体思路:首先,建成一个连通图,通过tarjan算法,然后对强连通子图进行缩点,对缩点后的“新”图来说,求出度为0的缩点中牛的数目。(这个题有一个坑点,就是条件是只要当前的这头牛被剩余的所有的牛仰慕就够了,它本身也可以再去崇拜别的牛,比如说 1  2 3构成一个强连通子图,输出结果应该是3,因为每一头牛都会被剩下的牛所仰慕。))

代码如下:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define maxn 50005
int low[maxn],dfn[maxn],vis[maxn],cnt[maxn],color[maxn],out[maxn];//low数组和dfn数组是tarjan算法的基本数组,vis数组是用来判断是否访问过的,cnt数组是用来存 染色后某一种具体颜色的点的个数,color数组是用来染色的,out数组是用来记录缩点之后,某一种颜色对应的出度
int num,ans;
vector<int>wakaka[maxn];
stack<int>q;
void tarjan(int u)
{
    vis[u]=1;
    q.push(u);
    low[u]=dfn[u]=++num;
    int len=wakaka[u].size();
    for(int i=0; i<len; i++)
    {
        int v=wakaka[u][i];
        if(vis[v]==0)
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        if(vis[v]==1)
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u])
    {
        int top;
        ans++;
        do
        {
            top=q.top();
            q.pop();
            vis[top]=-1;
            color[top]=ans//对同一个连通图里的字块进行染色;
        }
        while(top!=u);
    }
}

int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        ans=num=0;
        while(!q.empty())q.pop();
        for(int i=1; i<=n; i++)
        {
            wakaka[i].clear();
        }
        memset(vis,0,sizeof(vis));
        memset(cnt,0,sizeof(cnt));
        memset(color,0,sizeof(color));
        memset(out,0,sizeof(out));
        for(int i=1; i<=m; i++)
        {
            int u,v;
            cin>>u>>v;
            wakaka[u].push_back(v);
        }
        for(int i=1; i<=n; i++)
        {
            if(vis[i]==0)
            {
                tarjan(i);
            }
        }
        for(int i=1; i<=n; i++)
        {
        int t=color[i];
            int len=wakaka[i].size();
            for(int j=0; j<len; j++)
            {
           // cout<<i<<" "<<wakaka[i][j]<<endl;
                if(t!=color[wakaka[i][j]])
                {
                    out[t]++;//判断染色后某一个强连通子图的出度
                }
            }
            cnt[t]++;//记录某一个颜色下自快的个数
        }
        //cout<<ans<<endl<<color[1]<<endl<<color[2]<<endl<<color[3]<<endl;
        int x=0,temp;
        for(int i=1; i<=ans; i++)
        {
       // cout<<i<<" "<<out[i]<<" "<<cnt[i]<<endl;
            if(out[i]==0)
            {
                x++;
                temp=cnt[i];
            }
            //cout<<temp<<endl;
        }
        if(x==1)//只能有一个出度为0的缩点,如果有两个的话是肯定不成立的,打个比方,牛角,两边的端点都是出度为0,但是两边不互相承认对方为最强。
        {
            cout<<temp<<endl;
        }
        else
        {
            cout<<0<<endl;
        }
    }
    return 0;
}


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值