hdu3594-仙人掌图的判断&tarjan找环-Cactus

本文介绍仙人掌图的特点及Tarjan算法的应用,分析仙人掌图的三大特性,探讨Tarjan算法如何判断是否存在环,并提供两种实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://acm.hdu.edu.cn/showproblem.php?pid=3594
明确一点:一个scc并不一定是一个环,也可能是很多环。仙人掌图就是一个例子。
仙人掌图就是 每个环粘结起来,每条边只属于一个环。
有三个性质。
① 没有横向边(具体可以看见百度文库那个讲解,https://wenku.baidu.com/view/ce296043192e45361066f575.html)我理解的就是没有重复边(U,V一样)。
② 没有桥,因为有桥一定构不成SCC
③ u和u的子节点中,low【i】小于dfn[i] 不能大于等于2,存在一个,即意味着存在一个反向边。存在两个边即有两个环。看图这里写图片描述
然后讲一下我对tarjan的理解。
low 保存i点经过一定路径能够达到的最小dfn
dfn 保存i点的dfs顺序。
当low 和dfn相等时,即可以证明 i点是该scc dfs序最小的一个,(可以理解为一个scc中的提手。)4也算一个SCC。
这里写图片描述
所以可以用以下的方法计算。
① 如果SCC的数量大于1,则false
② 如果tarjan找到一个环,那么就记录这个环除了第一个dfn节点之外的所有节点。(因为其他点还会可能在其他环内,并且肯定做第一个点,,因为dfn的顺序。所以这样不会重复加。具体看图,第一个不计算红色的,而蓝的 绿的也只会在开始的时候记录一次。)
这里写图片描述
但是这样有一个错误的情况,明明不可以,但是输出是可以。。,wa了我好久。。
那就是出现重边的情况。酱紫(貌似一般的tarjan都会删除重边qwq)
这里写图片描述
解决办法。① 统计的时候,有重边,肯定不能构成仙人掌图。
② 在每个点统计的时候,即使他们没有构成环,如果子节点也在栈里,那么就 dfs统计。。这样肯定会把他们给加重了。就可以了qwq
给出两个解法,我窃以为第二种比较好,

#include <bits/stdc++.h>
/*  仙人掌图有以下性质。
   1   没有横向边,也就是用一个延时标记。
     或者  直接判断有无 重复边
   2 没有桥, 也就是low[v]<=dfn[u],如果存在low[v]>dfn[u]即false
     即 连通度为1(整个仙人掌图就是一个scc qwq)
   3 每个边只在一个环。1 方法1 统计u的每个子孩子的反向边
    直接统计他们的 前驱节点,把这个圈都 给记录一下
     但是不要计算 他们这个环中 那个dfn最小的点。
       因为如果他们 只是进行环与环普通粘结的话,也会
       当前环中的点就会被当做 第一个点。
*/
using namespace std;
const int maxn=2e5+5;
int low[maxn];// 最小能够到达的。
int dfn[maxn];//dfs的顺序
stack<int>s;//栈
bool state[maxn];//标记是否在栈里
int cnt[maxn];// 记录他每个点属于哪个scc
int num[maxn];// 记录他每个scc有多少点
int scc;// 记录scc的数量
int index;//记录dfs的顺序
bool flag; 
vector<int>G[maxn];
bool vis[maxn];
int tarjan(int u){
    dfn[u]=low[u]=++index;
    state[u]=true;
    s.push(u);
    int sum=0;
    for(int i=0;i<G[u].size();i++){
        int to=G[u][i];
        if(vis[to])
        {//printf("first %d\n",to);
            flag=false;} //性质1
        if(!dfn[to]){
            tarjan(to);
            if(low[to]>dfn[u])
            {  //printf("qiao?? %d %d\n",to,u);
                flag=false;}//有桥 性质2
        if(low[to]<dfn[u]) sum++;
           if(sum==2) flag=false;//性质3
           low[u]=min(low[to],low[u]);
        }
        else if(state[to]){
            sum++;
          low[u]=min(dfn[to],low[u]);
        if(sum==2)
            flag=false;
        }
        //vis[to]=true;
    }
    if(low[u]==dfn[u]){
         scc++;
          for(;;){
             int x=s.top();s.pop();
             state[x]=false;
             cnt[x]=scc;
             num[scc]++;
             if(x==u)break;
          }
    }
    vis[u]=true;
}
void init(){
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(state,false,sizeof(state));
    memset(cnt,0,sizeof(cnt));
    memset(num,0,sizeof(num));
    memset(vis,false,sizeof(vis));
    while(!s.empty()){
         s.pop();
    }
    scc=0;
    index=0;
    for(int i=0;i<maxn;i++)
        G[i].clear();
}
int main()
{   int t,m,a,b;
    scanf("%d",&t);
    while(t--){
          init();
          scanf("%d",&m);
          flag=true;
          while(~scanf("%d%d",&a,&b)){
               if(!a&&!b) break;
               //a++,b++;
               G[a].push_back(b);
          }
          for(int i=0;i<m;i++){
             if(!dfn[i])
                 tarjan(i);
          }
          if(flag&&scc==1)
            puts("YES");
          else
            puts("NO");
    }
    return 0;
}

2

#include<cstring>
#include<cstdio>
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e4+10;
const int maxm = 5e4+10;

struct st{
    int v,nex;
}edge[maxm];

int hed[maxn],vis[maxn],belon[maxn],in[maxn];
int low[maxn],dfn[maxn],stack1[maxn],fa[maxn];
int n,e,cnt,num,top,ok;

void add(int u,int v){
    edge[e].v=v,edge[e].nex=hed[u],hed[u]=e++;
}

void dfs(int u,int v){   //遍历环
    while(fa[u]!=v){
        in[u]++;
        if(in[u]>1){
            ok=0;
            return ;
        }
        u=fa[u];
    }
}

void targan(int u){      //targan判断是否联通
    if(!ok)return ;
    low[u]=dfn[u]=++num;
    stack1[top++]=u;
    vis[u]=1;
    for(int i=hed[u];~i;i=edge[i].nex){
        int v = edge[i].v;
        if(!dfn[v]){
            fa[v]=u;
            targan(v);
            if(low[v]<low[u])low[u]=low[v];
        }else if(vis[v]){
           low[u]=min(dfn[v],low[u]);
                dfs(u,v);  //由x沿路径返回并标记一直找到v
                if(ok==false)
                return ;
        }
    }

    if(dfn[u]==low[u]){
        cnt++;
        if(cnt>1){
            ok=0;
            return ;
        }
        int x ;
        do{
            x=stack1[--top];
            belon[x]=cnt;
            vis[x]=0;
        }while(x!=u);
    }
}


void init(){
    memset(dfn,0,sizeof(dfn));
    memset(hed,-1,sizeof(hed));
    memset(vis,0,sizeof(vis));
    memset(in,0,sizeof(in));
    e=1;
    cnt=top=num=0;
}

int main()
{
    int t;scanf("%d",&t);
    while(t--){
        init();
        scanf("%d",&n);
        int u,v;
        ok=1;
        while(scanf("%d%d",&u,&v),u+v){
                u++,v++;
            //for(int i=hed[u];~i;i=edge[i].nex){
               // if(edge[i].v==v)ok=0;
            //}
            add(u,v);
        }
        for(int i=1;i<=n&&ok;i++){
            if(!dfn[i])targan(i);
        }
        if(ok)printf("YES\n");
        else printf("NO\n");
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值