• 强连通分量Kosaraju算法


     基本思路:
      这个算法是最通用的算法,其比较关键的部分是同时应用了原图G和反图GT(步骤1)先用对原图G进行深搜记录时间结f(n)。(步骤2)选择f(n)最大的点在深搜得到各个强连通分量。
    2. 伪代码
      Kosaraju_Algorithm:
      step1:对原图G进行深度优先遍历,记录每个节点的离开时间。
      step2:选择具有最晚离开时间的顶点,对反图GT进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
      step3:如果还有顶点没有删除,继续step2,否则算法结束。
    如下图所视:
     
     

    poj2186
    #include <iostream>
    #include
    <cstring>
    #include
    <vector>
    using namespace std;
    const int MAX = 105;/****** * v为原图 * rv为所有边逆向后的新图 * s记录第一遍dfs后各点的结束时间:最晚结束的在栈顶 ******/
    vector
    <int> v[MAX], rv[MAX], s;/****** * num[v]点v所属连通分量的编号 * tree_n[i]第i个连通分量内点的个数 ******/
    int num[MAX], tree_n[MAX], cnt;
    bool mark[MAX],ml[MAX];
    void dfs_1(int x)//第一步计算dfs结束时间,将时间由小到大对应的数入s
    {
    mark[x]
    = 1;
    for (int i = 0; i < v[x].size(); ++i)
    {
    if (!mark[v[x][i]])
    {
    dfs_1(v[x][i]);
    }
    }
    s.push_back(x);
    }

    void dfs_2(int x)//从s的尾取数再rv中dfs
    {
    num[x]
    = cnt; //点v所属连通分量的编号为cnt
    ++tree_n[cnt]; //记录编号为cnt的连通分量中的元数个数
    mark[x] = 1;
    for (int i = 0; i < rv[x].size(); ++i)
    {
    if (!mark[rv[x][i]])
    {
    dfs_2(rv[x][i]);
    }
    }
    }
    void cal(int n)
    {
    int i,j;
    memset(mark,
    0, sizeof(mark));
    memset(ml,
    0,sizeof(ml));
    for (i = 1; i <= n; ++i)
    {
    for (j = 0; j < v[i].size(); ++j)
    {
    int x = v[i][j];
    if (num[i] != num[x])//检查强连通分量是否有出度,有就对应的mark[i]=1
    {
    mark[num[x]]
    = 1;
    ml[num[i]]
    =1;
    }
    }
    }
    int flag = 0,flg =0, ans;
    for (i = 1; i <= cnt; ++i) //检查有几个强连通分量的入度为0
    {
    if (!mark[i])
    {
    ans
    = i;
    ++flag;
    }
    if (!ml[i])
    {
    ++flg;
    }
    }
    cout
    <<flag<<endl;
    if(cnt!=1)
    {
    flg
    =flag>flg? flag:flg;
    cout
    <<flg<<endl;
    }
    else cout<<0<<endl;
    }

    int main()
    {

    int a, b, n, i, j;
    scanf(
    "%d",&n);
    for(i=1;i<=n;i++)
    {
    while(cin>>a&&a)
    {
    v[i].push_back(a);
    rv[a].push_back(i);
    }

    }

    for (i = 1; i <= n; ++i)
    {
    if (!mark[i])
    {
    dfs_1(i);
    }
    }
    memset(mark,
    0, sizeof(mark));
    memset(tree_n,
    0, sizeof(tree_n));
    cnt
    = 0; //cnt记录连通分量的个数
    for (i = s.size() - 1; i >= 0; --i)
    {
    if (!mark[s[i]])
    {
    ++cnt;
    dfs_2(s[i]);
    }
    }
    cal(n);
    return 0;
    }
    poj1236
    #include <iostream>
    #include
    <cstring>
    #include
    <vector>
    using namespace std;
    const int MAX = 10005;/****** * v为原图 * rv为所有边逆向后的新图 * s记录第一遍dfs后各点的结束时间:最晚结束的在栈顶 ******/
    vector
    <int> v[MAX], rv[MAX], s;/****** * num[v]点v所属连通分量的编号 * tree_n[i]第i个连通分量内点的个数 ******/
    int num[MAX], tree_n[MAX], cnt;bool mark[MAX];
    void dfs_1(int x)//第一步计算dfs结束时间,将时间由小到大对应的数入s
    {
    mark[x]
    = 1;
    for (int i = 0; i < v[x].size(); ++i)
    {
    if (!mark[v[x][i]])
    {
    dfs_1(v[x][i]);
    }
    }
    s.push_back(x);
    }

    void dfs_2(int x)//从s的尾取数再rv中dfs
    {
    num[x]
    = cnt; //点v所属连通分量的编号为cnt
    ++tree_n[cnt]; //记录编号为cnt的连通分量中的元数个数
    mark[x] = 1;
    for (int i = 0; i < rv[x].size(); ++i)
    {
    if (!mark[rv[x][i]])
    {
    dfs_2(rv[x][i]);
    }
    }
    }
    void cal(int n)
    {
    int i,j;
    memset(mark,
    0, sizeof(mark));
    for (i = 1; i <= n; ++i)
    {
    for (j = 0; j < v[i].size(); ++j)
    {
    int x = v[i][j];
    if (num[i] != num[x])//检查强连通分量是否有出度,有就对应的mark[i]=1
    {
    mark[num[i]]
    = 1;
    }
    }
    }
    int flag = 0, ans;
    for (i = 1; i <= cnt; ++i) //检查有几个强连通分量的入度为0
    {
    if (!mark[i])
    {
    ans
    = i;
    ++flag;
    }
    }
    if (flag == 1) //如果只有一个强连通分量的入度为0,则输出应的tree_n[i]为要的答案,否则没有
    {
    printf(
    "%d\n", tree_n[ans]);
    }
    else
    {
    printf(
    "0\n");
    }
    }

    int main()
    {

    int a, b, n, m, i, j;
    scanf(
    "%d%d", &n, &m);

    for (i = 0; i < m; ++i)
    {
    scanf(
    "%d%d", &a, &b);
    v[a].push_back(b);
    rv[b].push_back(a);
    }

    for (i = 1; i <= n; ++i)
    {
    if (!mark[i])
    {
    dfs_1(i);
    }
    }
    memset(mark,
    0, sizeof(mark));
    memset(tree_n,
    0, sizeof(tree_n));
    cnt
    = 0; //cnt记录连通分量的个数
    for (i = s.size() - 1; i >= 0; --i)
    {
    if (!mark[s[i]])
    {
    ++cnt;
    dfs_2(s[i]);
    }
    }
    cal(n);
    return 0;
    }
  • 相关阅读:
    9.堆排序
    8.全排列
    37.微信跳一跳辅助开发(C语言+EasyX)
    7.图形化实现快速排序法
    codeforces 632A A. Grandma Laura and Apples(暴力)
    codeforces 633D D. Fibonacci-ish(dfs+暴力+map)
    codeforces 633B B. A Trivial Problem(数论)
    codeforces 633A A. Ebony and Ivory(暴力)
    codeforces 622B B. The Time
    codeforces 622D D. Optimal Number Permutation(找规律)
  • 原文地址:https://www.cnblogs.com/qijinbiao/p/2127893.html
Copyright © 2020-2023  润新知