zoukankan      html  css  js  c++  java
  • 算法浅谈之割点

    割点的概念

    在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点(cut vertex / articulation point)。

    例如,在下图中,0、3是割点,因为将0和3中任意一个去掉之后,图就不再连通。如果去掉0,则图被分成1、2和3、4两个连通分量;如果去掉3,则图被分成0、1、2和4两个连通分量。

    404

    怎么求割点

    可以使用Tarjan算法求割点(注意,还有一个求连通分量的算法也叫Tarjan算法,与此算法类似,参见here)。(Tarjan,全名Robert Tarjan,美国计算机科学家。)

    首先选定一个根节点,从该根节点开始遍历整个图(使用DFS)。

    对于根节点,判断是不是割点很简单——计算其子树数量,如果有2棵即以上的子树,就是割点。因为如果去掉这个点,这两棵子树就不能互相到达。

    对于非根节点,判断是不是割点就有些麻烦了。我们维护两个数组dfn[]low[]dfn[u]表示顶点u第几个被(首次)访问,low[u]表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。

    但这里也出现一个问题:怎么计算low[u]

    假设当前顶点为u,则默认low[u]=dfn[u],即最早只能回溯到自身。

    有一条边(u, v),如果v未访问过,继续DFSDFS完之后,low[u]=min(low[u], low[v])

    如果v访问过(且u不是v的父亲),就不需要继续DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])

    参考博文:https://www.cnblogs.com/collectionne/p/6847240.html

    代码

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=20000;
    const int MAXM=100000+10;
    int dfn[MAXN],low[MAXN];
    bool cut[MAXN];
    int ans;
    int n,m,deep;
    struct Node
    {
    	int to,next;
    }edge[MAXM*2];
    int cnt,head[MAXN];
    inline int read()
    {
    	int tot=0;
    	char c=getchar();
    	while(c<'0'||c>'9')
    		c=getchar();
    	while(c>='0'&&c<='9')
    	{
    		tot=(tot<<1)+(tot<<3)+c-'0';
    		c=getchar();
    	}
    	return tot;
    }
    inline void add(int x,int y)
    {
    	edge[++cnt].to=y;
    	edge[cnt].next=head[x];
    	head[x]=cnt;
    }
    inline void tarjan(int u,int father)
    {
    	int tot=0;
    	low[u]=dfn[u]=++deep;
    	for(int i=head[u];i;i=edge[i].next)
    	{
    		int v=edge[i].to;
    		if(!dfn[v])
    		{
    			tarjan(v,u);
    			low[u]=min(low[u],low[v]);
    			if(low[v]>=dfn[u]&&u!=father)cut[u]=1;
    			if(u==father)tot++;
    		}
    		low[u]=min(low[u],dfn[v]);
    	}
    	if(u==father&&tot>=2)cut[u]=1;
    }
    int main()
    {
    	n=read();m=read();
    	for(int i=1;i<=m;i++)
    	{
    		int x=read(),y=read();
    		add(x,y);add(y,x);
    	}
    	/*for(int i=1;i<=n;i++)
    		for(int j=head[i];j;j=edge[j].next)
    			cout<<i<<" "<<edge[j].to<<endl;*/
    	for(int i=1;i<=n;i++)
    		if(!dfn[i])tarjan(i,i);
    	for(int i=1;i<=n;i++)	
    		ans+=cut[i];
    	printf("%d
    ",ans);
    	for(int i=1;i<=n;i++)
    		if(cut[i])printf("%d ",i);
    	printf("
    ");
    	return 0;
    }
    

    下面是一些练习

  • 相关阅读:
    在Android模拟器(BlueStacks)中调试录音应用【20121221】
    一路来的脚印_停第二念【20130109】
    Office集锦_Word_禁止在粘帖内容前后添加空格【201318】
    Libgdx使用过程中在Android环境下遇到的各种错误提示的解决【2012112】
    童真趣语【20121217】
    【转】Android中的资源分析、读取【2012113】
    Office集锦_Word_禁止拼写检查【201318】
    BlueStacks中用户自己的SdCard的位置【2012116】
    Eclipse+BlueStacks调试Android应用_补充篇【20121030】
    The Power of H3D2 ! 截图,视频!
  • 原文地址:https://www.cnblogs.com/hulean/p/11919835.html
Copyright © 2011-2022 走看看