强连通分量学习记录

近期为了弥补之前知识面窄的尴尬,学了一点图论的内容。强连通分量算是用的很多的一个图论算法。

强联通分量的作用在于它的性质,每个强连通分量内的任意两点都可以任意到达。这个性质有些时候对解题会很有帮助,其次它还可以帮我们完成缩点的作用。缩点之后整个图就会变成一棵树,从而使我们的解题可以变得比较方便。

大概关于强连通分量的理解就这么多,具体可以看这篇博客有详细的解释和例题:

https://blog.csdn.net/qq_34374664/article/details/77488976

第一道简单的例题:

POJ:2186

题目大意:

经典奶牛题目,就是A喜欢B,B喜欢C,则可以推出A喜欢C,那么问有多少头奶牛是被所有奶牛喜欢的。

解题思路:

首先把整个图缩点,每个强连通分量内的两点都可任意到达,所以一个强连通分量被所有其他奶牛喜欢即它里面任意一头奶牛都被任意其他奶牛喜欢。

那么问题就在于怎么知道那个强连通分量是被所有其他奶牛喜欢的,其实很简单,因为我们得到的是一个树结构,所以可以得出那些出度为0的点即叶子结点是被上面所有的强连通分量喜欢的,但是这样的结点有且只能有一个,才能保证这个结点的奶牛被其他所有奶牛喜欢。

Ac代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e4+5;
const int INF=1e9+7;
const ll mod=998244353;
int n,m,tot,cnt,dfn[maxn],low[maxn],belong[maxn],size[maxn];
vector<int> v[maxn],rv[maxn];
bool vis[maxn];
stack<int> s;
void tarjan(int u)  //tarjan算法
{
    dfn[u]=low[u]=++tot;
    vis[u]=1,s.push(u);
    for(int i=0;i<v[u].size();i++)
    {
        int g=v[u][i];
        if(!dfn[g])
        {
            tarjan(g);
            low[u]=min(low[u],low[g]);
        }
        else if(vis[g]) low[u]=min(low[u],dfn[g]);
    }
    if(dfn[u]==low[u])
    {
        cnt++;
        while(s.top()!=u)
        {
            size[cnt]++;
            belong[s.top()]=cnt;
            vis[s.top()]=0,s.pop();
        }
        size[cnt]++;
        belong[s.top()]=cnt;
        vis[s.top()]=0,s.pop();
    }
}
void rebuild()  //缩点后重建
{
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<v[i].size();j++)
        {
            int u=v[i][j];
            if(belong[i]==belong[u]) continue;  //跳过无用边
            rv[belong[i]].push_back(belong[u]);
        }
    }
    for(int i=1;i<=cnt;i++) //去掉重复边
        sort(rv[i].begin(),rv[i].end()),rv[i].erase(unique(rv[i].begin(),rv[i].end()),rv[i].end());
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i]) tarjan(i);
    rebuild();
    int res1=0,res=0;
    for(int i=1;i<=cnt;i++) //判断出度为0的连通分量的个数
        if(rv[i].size()==0) res1++,res=i;
    if(res1==1) printf("%d\n",size[res]);
    else printf("0\n");
    //system("pause");
}

猜你喜欢

转载自blog.csdn.net/f2935552941/article/details/81071024