图的连通性以及割点

本文深入探讨了如何使用Floyd算法判断有向图的单连通性,并通过tarjan算法验证图是否为强连通图。通过Floyd算法的位运算优化,实现了一种更为高效的判断方法。此外,文章还阐述了在有向无环图(DAG)中判断单向联通性的方法,以及如何利用tarjan算法缩点简化问题。
摘要由CSDN通过智能技术生成

首先明白几个定理:

  • 连通分量:无向图 G 的一个极大连通子图称为 G 的一个连通分量(或连通分支)。连通图只有一个连通分量,即其自身;非连通的无向图有多个连通分量。
  • 强连通图:有向图 G=(V,E) 中,若对于V中任意两个不同的顶点 x 和 y ,都存在从x 到 y 以及从 y 到 x 的路径,则称 G 是强连通图(Strongly Connected Graph)。相应地有强连通分量(Strongly Connected Component)的概念。强连通图只有一个强连通分量,即是其自身;非强连通的有向图有多个强连通分量

1. 判断无向图的连通性。

   用bfs或者dfs,遍历,如果能遍历完所有结点,则是连通图。

  比如,要求无向图的割点的集合,最容易想到的方法就是依次测试每个结点判断其对图连通性的影响。

  有更简单的方法。


2. 有向图的单连通性。对于任意的两个点,i,j,存在i到j或者j到i的路径

  1. 最容易想到的算法是floyd算法。

      floyd算法本来用以求图中任意两点间的最短路径,大体思路是:

     依次以每个点k作为i--->j的中间节点,求d(i,j) = min(d(i,j),d(i,k)+d(k,j))

    如果用floyd算法判断任意两个点的连通性?

    可以直接用上面的算法,但是可以将加法和min操作转为位运算。

    d(i,j) = d(i,j) | (d(i,k) & d(k,j))

 2. 上面的算法时间复杂度是O(n^3)

     http://hi.baidu.com/735612658gfy/item/e2e8c8d3362fa9f2b3f7777b

   

思路:判断一个有向图是不是强连通的可以直接用tarjan算法。

          但是判断是不是单向联通的就麻烦了。

          开始我想用floyd,后来又想到了一条路一条路的找,但是这两种方法的复杂度太高。

正解:对于一个DAG图,若是单向联通的我们可以有这么一个性质,就是存在一条路,这条路可以经过图中的每一个点。下面证明一下。

我们从图中选择一个入度为0的点a,和一个出度为0的点b,则剩下的点肯定都满足在a,b之间。

不妨找一点k ,则a~k~b,剩下的点也肯定在两个~之间的一个,以此类推。

我们先让这张图变成DAG,我们用tarjan算法缩点,这张图就变成了一个DAG图。


[cpp]  view plain copy
  1. #include<cstdio>  
  2. #include<iostream>  
  3. #include<cstring>  
  4. #include<stack>  
  5. using namespace std;  
  6. stack<int>s;  
  7. struct hh  
  8.  {  
  9.      int u,v,next;  
  10.  }tp[6005],map[6006];  
  11.  int head[1050],mhead[1050],num,mnum,low[1050],lp[1050],now,f[1050];  
  12.  bool in[1050];  
  13.  int n,m,step,total;  
  14.  void add(int a,int b)  
  15.   {  
  16.       tp[num].u=a;tp[num].v=b;  
  17.       tp[num].next=head[a]; head[a]=num++;  
  18.   }  
  19.   void madd(int a,int b)  
  20.    {  
  21.        map[mnum].u=a; map[mnum].v=b;  
  22.        map[mnum].next=mhead[a]; mhead[a]=mnum++;  
  23.    }  
  24.  void init()  
  25.   {  
  26.       int a,b;  
  27.       num=0; mnum=0;  
  28.       step=total=1;  
  29.       while(!s.empty()) s.pop();  
  30.       for(int i=1;i<=n;i++)  
  31.        {  
  32.            in[i]=0;  
  33.            low[i]=lp[i]=f[i]=0;  
  34.        }  
  35.        memset(head,-1,sizeof(head));  
  36.        memset(mhead,-1,sizeof(mhead));  
  37.       scanf("%d%d",&n,&m);  
  38.       for(int i=0;i<m;i++)  
  39.        {  
  40.            scanf("%d%d",&a,&b);  
  41.            add(a,b);  
  42.        }  
  43.   }  
  44.  void tarjan(int u)  
  45.   {  
  46.       int v;  
  47.       low[u]=lp[u]=step++;  
  48.       s.push(u); in[u]=1;  
  49.       for(int i=head[u];i!=-1;i=tp[i].next)  
  50.        {  
  51.            v=tp[i].v;  
  52.            if(!low[v])  
  53.             {  
  54.                 tarjan(v);  
  55.                 low[u]=min(low[u],low[v]);  
  56.             }  
  57.             else if(in[v])  
  58.             {  
  59.                 low[u]=min(low[u],lp[v]);  
  60.             }  
  61.        }  
  62.        if(low[u]==lp[u])  
  63.         {  
  64.             int haha;  
  65.             while(1)  
  66.              {  
  67.                  haha=s.top(); s.pop();  
  68.                  f[haha]=total; in[haha]=0;  
  69.                  if(haha==u)break;  
  70.              }  
  71.              total++;  
  72.         }  
  73.   }  
  74.   void suodian()  
  75.    {  
  76.        for(int i=0;i<num;i++)  
  77.         {  
  78.             if(f[tp[i].u]!=f[tp[i].v])  
  79.             madd(f[tp[i].u],f[tp[i].v]);  
  80.         }  
  81.    }  
  82.    bool can[1004][1004];  
  83.    int b[1050];  
  84.    bool  dfs(int u,int now)  
  85.    {  
  86.        in[u]=1;  
  87.        if(now==total-1)return true;  
  88.        bool flag=0;  
  89.        for(int i=mhead[u];i!=-1;i=map[i].next)  
  90.         {  
  91.             flag=dfs(map[i].v,now+1);  
  92.             if(flag)return true;  
  93.         }  
  94.         return false ;  
  95.    }  
  96.    void result()  
  97.     {  
  98.        memset(in,0,sizeof(in));  
  99.        for(int i=1;i<=n;i++)  
  100.         {  
  101.             if(!in[i])  
  102.              {  
  103.                  bool flag=dfs(i,1);  
  104.                   {  
  105.                       if(flag)  
  106.                       {cout<<"Yes"<<endl; return ;}  
  107.                   }  
  108.              }  
  109.         }  
  110.         cout<<"No"<<endl;  
  111.     }  
  112.  int main()  
  113.   {  
  114.       int t;  
  115.       scanf("%d",&t);  
  116.       while(t--)  
  117.        {  
  118.            init();  
  119.            for(int i=1;i<=n;i++)  
  120.             if(!low[i])  
  121.              tarjan(i);  
  122.            suodian();//重新建图  
  123.            result();  
  124.        }  
  125.   }  

也可以用上面的算法求出有向图的单连通分支, 对入度为0点,递归搜寻各向前通路至每一不可再前行点时,可对应得出一单项连通分支


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值