• 无向图求点割集的算法


    http://blog.csdn.net/xinghongduo/article/details/6202646
    
    黑书上给出了关于求点割集的算法,但是比较模糊,我查阅了网络上的相关资料,理解了求点割集的过程,写出如下求点割集的代码,并写了一些简单的证明.
     
    割点集的定义:如果在连通图G中去掉某一点后图不连通,那么这个点即为G的割点,所有割点的集合即为点割集。
     
    求点割集的方法:利用tarjan算法的思想,用数组dfn[v]存储DFS遍历到点v的时间,数组low[v]存储点v能追溯到最早的祖先节点。
    如果对于点v来说有如下结论:
     
     
    1.如果点v是DFS序列的根节点,则如果v有一个以上的孩子,则v是一个割点。
    2.如果v不是DFS序列根节点,并且点v的任意后继u能追溯到最早的祖先节点low[u]>=dfn[v],则v是一个割点。
     
     
    证明1:
      
           假设DFS遍历的第一个节点v不是割点,那么则有low[v]=dfn[v]=1,继续对v的孩子节点u遍历,必然有low[u]>=dfn[v],按照第二条性质,则v是割点,但我们已经假设v不是割点。这是由于v是DFS遍历的起始节点,在遍历序列中v没有祖先节点,v的所有后继节点能追溯到最早的祖先节点最多也就是v了,不可能比v再早了,因此必须把DFS遍历的第一个节点v单独考虑,那么怎么判断v是不是割点呢?
    
          例如上图,设v是DFS序列访问的第一个节点,对v的孩子节点u和u的所有孩子节点进行DFS遍历并标记为已经访问后,如果v的另一个孩子节点k没有被标记为已经访问,那么u和k之间一定不存在边,也就是说u和k之间的连通必然需要点v,因此如果v是DFS遍历的第一个节点,对v是否为割点的判断方法是:看v是不是有多个孩子,如果有则v是割点。
    证明2:
          如果v不是DFS遍历的第一个节点,那么对于v的所有后继节点来说,如果v不是割点(也就是如果删掉点v,剩下的图还是连通图),那么v的后继节点必然能追溯到DFS遍历序列中v的祖先节点,也就是v的后继节点中存在到达DFS序列中v的祖先的路径,因此当DFS回溯到v节点时对于v的所有后继节点u来说,都有low[u]<dfn[v]。
          如果v是一个割点,对所有v的后继节点u进行DFS后,必然有low[u]>=dfn[v],这是因为,当遍历v并将其锁定后,到达v的祖先节点的路径已经被封死,v的后继节点必然不可能访问到v的祖先节点,因此,必然有low[u]>=dfn[v]。
     
     
    有了上面的分析,下面写出求无向图点割集的代码:
     
     #include<iostream>
    using namespace std;
    
    struct L
    {
    	int v;	L *next;
    };
    
    class HEAD
    {
    public:
    	int id;	L *next;
    	HEAD(){	id=0;	next=NULL;}
    };
    
    
    HEAD head[1000];  int dfn[1000],low[1000],t;     bool lock[1000],C[1000];
    
    
    void find(int father,int v)
    {
        int count=0;			/*统计v的孩子数*/
    	dfn[v]=low[v]=++t;     /*将访问时间赋给dfn[v]和low[v]*/
    	lock[v]=false;      /*标记v点已经访问过,不能再被访问*/
    	for(L *p=head[v].next;p!=NULL;p=p->next)
    	{
    		if(lock[p->v])  /*如果v的直接后继节点没有访问过,则对其遍历*/
    		{
    			find(v,p->v);  /*对v的直接后继遍历*/
    			count++;        /* 孩子数+1 */
    			if(low[v]>low[p->v])  /*如果v的孩子能追溯到更早的祖先,则v也能追溯到*/
    				low[v]=low[p->v];
    		}
    		else if(p->v!=father&&low[p->v]<low[v])  /*如果v的直接孩子节点已经被访问过*/
    			low[v]=low[p->v];
    
    		if(!father&&count>1)		/*如果当前节点是DFS遍历到的第一个节点,则判断其是否有多个孩子*/
    			C[v]=true;
    		else if(father&&dfn[v]<=low[p->v])   /*否则判断其后继能否追溯到v的祖先*/
    			C[v]=true;
    	}
    }
    
    
    int main()
    {
    	int n,i,a,b;
    	cin>>n;
    	
    	while(cin>>a>>b&&a&&b)		/*建立邻接表,输入无向图边每条a b,以0 0结束*/
    	{
    		L *p=new L;
    		p->next=head[b].next;
    		head[b].next=p;
    		p->v=a;
    		p=new L;
    		p->next=head[a].next;
    		head[a].next=p;
    		p->v=b;
    		head[b].id++;
    		head[a].id++;
    	}
    	memset(lock,true,sizeof(lock));
    	memset(dfn,0,sizeof(int)*1000);
    	memset(C,0,sizeof(C));  /*C数组用来标记那些点是割点,刚开始全部置为false*/
    	t=0;   /*访问时间*/
    	find(0,1);/*开始对1号点DFS,第一个遍历的前驱节点设为0*/
    	for(i=1;i<=n;i++)		/*输入割点*/
    		if(C[i])
    			cout<<i<<' ';
    		cout<<endl;
    		
    }

  • 相关阅读:
    异常介绍
    docker 命令
    acm
    Openfiler能把标准x86/64架构的系统变成一个强大的NAS、SAN存储和IP存储网关
    docker 图解学习
    基于Docker的TensorFlow机器学习框架搭建和实例源码解读
    菜鸟打印控件
    Oracle 12c on Solaris 10 安装文档
    内存对齐小解
    安装oracle 11gr2 rac on solaris
  • 原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410700.html
Copyright © 2020-2023  润新知