zoukankan      html  css  js  c++  java
  • 图论及其应用——连通性问题

      我们这篇文章将会开始讨论关于图的连通性的问题。
      对于图的连通性问题,有着怎样的实际应用呢?其实很明显的一点就是社交网络、朋友圈当中的应用,后台可以根据一个连通分量(在实际的模型可以认为有着某些共性)当中其他用户的一些数据,来推测该连通分量下某一个用户可能感兴趣的信息。
      首先,我们给出图的强连通性的定义,对于图G<V,E>,如果其子图G<V',E'>满足对于任意vi、vj∈V',vi、vj之间总有一条通路,那么G'则称作G的一个强连通分量。
      目前对于一个图的最大强连通分量的求解,比较常见的是Kosaraju算法、Tarjan算法和Garbow算法,它们统称为SCC(strongly connected components)算法。
      下面我们来介绍Kosaraju算法。
      首先,我们要知道Kosaraju算法基于一条很重要也很显然的定理。
      定义:给定一个有向图G,若将G中所有的边的方向逆置,得到的图GT成为G图的逆图。
      定理:图G的逆图GT的强连通分量的个数是一样的。
      基于这个定理,我们对于G图,通过dfs来遍历G中所有的点,并在遍历过程中进行标记,我们会得到这样一个序列:<v1、v2、v3、v4……vn>,我们假设G并不是连通的,在dfs遍历的时候,必然有vi、vi+1之间是没有直接的通路的,但是<v1、v2、……vi>却是通过dfs给出的一条真实存在的路径,也就是说,我们得到的点序列<v1、v2、v3……vn>中包含着多个连通分量,并且对于连通分量的路径<vi、vi+1……vj>中,取出任意两点,下标小的点到下标大的点是一定存在通路的(这取决于这条路径是由遍历图的dfs算法得到的)。
      基于对<v1、v2、v3……vn>含义的理解,我们现在将图进行转置(得到逆图GT),然后按照这样一个序列:<vn、……v3、v2、v1>的进行遍历,假设当前遍历到了vi点,则表明逆图GT中vn->vi可达,也就是说原图中vi->vn可达,那么我们现在只需证明原图中vn->vi可达,再根据强连通的定义,即可以说vn、vi属于一个强连通分量。
      我们利用反证法,如果原图中不存在vn->vi的通路,而存在vn->vi的通路,根据我们对<v1、v2、v3……vn>含义的理解,我们容易基于这种假设,i>n,这与事实是不符的,因此得证。
      那么我们从vn开始,遍历逆图GT上能够遍历到的点,那么就得到了vn所在的最大强连通分量,一次进行下去,我们就可以找到所有的最大强连通分量了。
      概括一下该算法,便会得到如下过程。
      (1)对原图G进行dfs,记录每个节点离开的时间num[i](实际上就是得到上面所说序列)。
      (2)选择具有最晚离开时间的顶点,对逆图GT进行dfs,能够遍历到的点与该点组成一个强连通分量。
      (3)如果点集中还有点剩余,重复第二步。
      我们通过一个题目来具体的实现该算法。(Problem source : hdu 1269)
     

    Problem Description
    为了训练小希的方向感,Gardon建立了一座大城堡,里面有N个房间(N<=10000)和M条通道(M<=100000),每个通道都是单向的,就是说若称某通道连通了A房间和B房间,只说明可以通过这个通道由A房间到达B房间,但并不说明通过它可以由B房间到达A房间。Gardon需要请你写个程序确认一下是否任意两个房间都是相互连通的,即:对于任意的i和j,至少存在一条路径可以从房间i到房间j,也存在一条路径可以从房间j到房间i。
     
    Input
    输入包含多组数据,输入的第一行有两个数:N和M,接下来的M行每行有两个数a和b,表示了一条通道可以从A房间来到B房间。文件最后以两个0结束。
     
    Output
    对于输入的每组数据,如果任意两个房间都是相互连接的,输出"Yes",否则输出"No"。


      数理分析:容易看到,这道题目是判断一个图是否具有强连通性,及判断给定图G是否有且仅有一个强连通分量。
      通过上文对算法的分析,我们利用c++中的stl中的一些工具对算法进行编程实现。(为防止储存图信息的二维数组爆掉内存限制,我们这里用到栈来储存图)
      参考代码如下。

    #include <cstdio>
    #include <stack>
    #include <vector>
    
    using namespace std;
    
    const int N = 10005;
    
    vector<int> adj[N], t_adj[N];
    bool visit[N];
    int n, m, cnt;
    
    //利用非递归的DFS来防止MLE
    //通过传入的是adj或者t_adj来进行两遍DFS
    bool dfs(int rt, vector<int> *graph)
    {
        stack<int> st;
        st.push(rt);
        visit[rt] = true;
        cnt = 0;//用来统计从rt能访问到图中节点的个数
    
        while (!st.empty())
        {
            int u = st.top();
            ++cnt;
            st.pop();
            
            for (int i = 0; i < graph[u].size(); ++i)
            {
                if (visit[graph[u][i]])continue;
                st.push(graph[u][i]);
                visit[graph[u][i]] = true;
            }
    
        }
        if (cnt == n)return true;
        return false;
    }
    
    //将图做转置
    void transform()
    {
        for (int i = 1; i <= n; ++i)
        {
            for (unsigned j = 0; j < adj[i].size(); ++j)
            {
                t_adj[adj[i][j]].push_back(i);
            }
        }
    }
    
    bool kosaraju()
    {
        memset(visit, 0, sizeof(visit));
    
        bool ret = dfs(1, adj);
        if (!ret)return false;
    
        transform();
        memset(visit, 0, sizeof(visit));
    
        ret = dfs(1, t_adj);
        if (ret)return true;
        return false;
    }
    
    int main()
    {
        int a, b;
    
        while (scanf("%d %d", &n, &m) != EOF && (m + n))
        {
            for (int i = 0; i < m; ++i)
            {
                scanf("%d %d", &a, &b);
                adj[a].push_back(b);
            }
    
            if (kosaraju())
            {
                printf("Yes
    ");
            }
            else
            {
                printf("No
    ");
            }
    
            for (int i = 1; i <= n; ++i)
            {
                adj[i].clear();
                t_adj[i].clear();
            }
        }
        return 0;
    }


     
     

  • 相关阅读:
    python基础代码2
    将博客搬至CSDN
    Python基础代码1
    使用函数处理数据
    创建计算字段
    用通配符进行过滤
    高级过滤数据
    过滤数据
    模式与架构
    工厂方法模式和简单工厂模式的选折
  • 原文地址:https://www.cnblogs.com/rhythmic/p/5360546.html
Copyright © 2011-2022 走看看