poj3713 Transferring Sylla 枚举+tarjan判割点

其实就是判断是否为三连通图

三连通图指的是去掉3个点就不连通的图,但是并没有直接求三连通的算法。著名的Tarjan算法可以求解连通和割点,再枚举删除一个点就能达到三连通的目的。

先看用例2,是由用例1去掉一条边而变成非三连通图的:

至少造成了2和3非三连通:

我们来思考如何推导出2和3非三连通,假设从上图中删除了节点0,通过Tarjan算法,我们可以发现节点1是割点:

 

那么只需删除从3到割点和从3到我们枚举删除的节点0的两条边,就可以将3和2分割开来:

才删除了两条边2和3就不连通了,这个图显然不是三连通图。

 

#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 20010;
int cnt,flag,times,del,root;
int head[maxn],low[maxn],dfn[maxn];
struct no
{
    int v,next;
}Eg[2*maxn];
void init( )
{
    cnt=0;
    memset(head,-1,sizeof(head));
}
void add(int form,int to)
{
    Eg[cnt].v=to;Eg[cnt].next=head[form];head[form]=cnt++;
}
void dfs(int u,int fa)
{
    if(flag)
    return ;
    int tot=0;
    low[u] = dfn[u] = ++times;
    for(int i=head[u] ; i!=-1 ; i=Eg[i].next)
    {
        int v=Eg[i].v;
        if(v==fa||v==del)
        continue;
        if(!dfn[v])
        {
            tot++;
            dfs(v,u);
            low[u]=min(low[u],low[v]);
            //判断割点
            if((u==root&&tot>1)||(u!=root&&low[v]>=dfn[u]))
                flag=1;
        }
        else
        low[u]=min(low[u],dfn[v]);
    }
}
int main( )
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
            break;
    init();
    for(int i=0 ; i<m ; i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    flag=0;
    for(int i=0 ; i<n ; i++)
    {
        del = i ; times = 0;
        memset(dfn,0,sizeof(dfn));
        root = 0;
        if(del==0)
        root=1;
        dfn[del] = 1;
        dfs(root,-1);
        for(int j=0 ; j<n ; j++)
        {
            if(!dfn[j])
            {
                flag=1;
                break;
            }
        }
        if(flag)
            break;
    }
    if(flag)
        puts("NO");
    else
        puts("YES");
    }
    return 0;

}
View Code

 

posted @ 2018-07-14 14:01  shuai_hui  阅读(237)  评论(0编辑  收藏  举报