【小结】强联通分量分解

强联通分量

  • 在一个有向图的顶点子集 S 中,对(u,v),如果都能找到一条从 u v的路径,那么就称 S 是强联通的。如果向S中加入任何一个其他顶点后 S 都不再是强联通的,就称S时原图的一个强联通分量。
  • 显然,如果把所有的强联通分量都缩点,原图将变成一个 DAG
  • SCC 的求解可通过两次 dfs 实现,第一次在原图中后续遍历,标号;第二遍将所有边反向后,从编号最大的点开始遍历,每次都可得到一个 SCC
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MAX_V = 1024;
vector<int> G[MAX_V];
vector<int> rG[MAX_V]; //rev-G
vector<int> vs; //node-list while post-visit
bool used[MAX_V]; //flag
int cmp[MAX_V]; //tupper

int V; //node

void addEdge(int from, int to)
{
    G[from].push_back(to);
    rG[to].push_back(from);
}

void dfs(int v)
{
    used[v] = true;
    for (int i = 0; i < G[v].size(); ++i)
    {
        if (!used[G[v][i]])
        {
            dfs(G[v][i]);
        }
    }
    vs.push_back(v);
}

void rdfs(int v, int k)
{
    used[v] = true;
    cmp[v] = k;
    for (int i = 0; i < rG[v].size(); ++i)
    {
        if (!used[rG[v][i]])
        {
            rdfs(rG[v][i], k);
        }
    }
}

int scc()
{
    memset(used, false, sizeof(used));
    vs.clear();
    for (int v = 0; v < V; ++v)
    {
        if (!used[v])
        {
            dfs(v);
        }
    }
    memset(used, false, sizeof(used));
    int k = 0;
    for (int i = vs.size() - 1; i >= 0; --i)
    {
        if (!used[vs[i]])
        {
            rdfs(vs[i], k++);
        }
    }
    return k;
}

int main()
{
    return 0;
}
  • 问有几只奶牛受欢迎
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAX = 10007;

vector<int> G[MAX];
vector<int> rG[MAX];
stack<int> S;
bool vis[MAX];
int cmp[MAX];
int N, M;

inline void add_edge(int x, int y)
{
    G[x].push_back(y);
    rG[y].push_back(x);
}

void dfs(int v)
{
    vis[v] = true;
    for (int i = 0; i < G[v].size(); ++i)
    {
        if (!vis[G[v][i]])
        {
            dfs(G[v][i]);
        }
    }
    S.push(v);
}

void rdfs(int v, int k)
{
    vis[v] = true;
    cmp[v] = k;
    for (int i = 0; i < rG[v].size(); ++i)
    {
        if (!vis[rG[v][i]])
        {
            rdfs(rG[v][i], k);
        }
    }
}

int scc()
{
    memset(vis, false, sizeof(vis));
    for (int i = 1; i <= N; ++i)
    {
        if (!vis[i])
        {
            dfs(i);
        }
    }

    int k = 0;
    memset(vis, false, sizeof(vis));
    while (!S.empty())
    {
        int to = S.top();
        S.pop();

        if (!vis[to])
        {
            rdfs(to, ++k);
        }
    }
    return k;
}

void travel(int v)
{
    vis[v] = true;
    for (int i = 0; i < rG[v].size(); ++i)
    {
        if (!vis[rG[v][i]])
        {
            travel(rG[v][i]);
        }
    }
}

int gao()
{
    int cnt = scc();
    /*
        printf("cnt = %d\n", cnt);
        for (int i = 1; i <= N; ++i)
        {
            printf("cmp[%d] = %d\n", i, cmp[i]);
        }
    */
    for (int i = 1; i <= N; ++i)
    {
        if (cmp[i] == cnt)
        {
            memset(vis, false, sizeof(vis));
            travel(i);

            int res = 0;

            for (int j = 1; j <= N; ++j)
            {
                if (!vis[j])
                {
                    return 0;
                }
                if (cmp[j] == cnt)
                {
                    ++res;
                }
            }
            return res;
        }
    }
    return 0;
}

int main()
{
    while (~scanf(" %d %d", &N, &M))
    {
        for (int i = 1; i <= N; ++i)
        {
            G[i].clear();
            rG[i].clear();
        }

        int A, B;
        while (M--)
        {
            scanf(" %d %d", &A, &B);
            add_edge(A, B);
        }

        printf("%d\n", gao());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值