Hdu 1269 迷宫城堡 —— 构造树求强联通

82 篇文章 1 订阅

This way

题意:

给你一张有向图,问你这个图是否任意两点之间都能够相互到达

题解:

    好像是Tarjan的模板题,已经不记得这个知识点了。况且一题多解的能力是必要的.
    看到这道题第一反应就是首先在图中找到一棵有向树包含所有节点,且如果这个图是强联通的话是必然能实现的。然后呢?
    在有向树上选择一个点x能够到达其它任意一个非同子树点y的必要条件就是x的子树上有一条边连到y和y的所有祖先节点中任意一个。
    考虑y必然需要是根节点,同时x必然有叶子节点的情况,所以这道题变成了找到一棵有向树联通所有点,并且使叶子节点能够都连到根。
    但是它本质是一张图,所以叶子节点之间也可能相互存在边,但是我们要做的事情还是看是否所有叶子能够连到根。多源点找单点是否都联通可能不太好找,但是找单源点能否访问其它点就只需要一个dfs即可。
    所以我的思路是先构造一个树,然后建立反向边,如果根通过反向边能访问所有叶子,那么就是一个强连通图。
    如何构造一棵树?由于确定的答案是强联通,那么任意一个点都能成为树根。dfs1找出所有的叶子即可。

正向用链式前向星,反向我用了动态数组,不必要。

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=1e5+5;
struct node{
    int to,next;
}e[M];
int head[N],cnt;
void add(int x,int y){
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt++;
}
int leaf[N],vis[N];
int dfs1(int x){
    int f=0;
    for(int i=head[x];~i;i=e[i].next){
        int ne=e[i].to;
        if(vis[ne])continue;
        vis[ne]=f=1;
        dfs1(ne);
    }
    if(!f)leaf[x]=1;
}
vector<int>pre[N];
int dfs2(int x){
    for(auto ne:pre[x]){
        if(vis[ne])continue;
        vis[ne]=1;
        dfs2(ne);
    }
}
int main()
{
    int n,m,x,y;
    while(~scanf("%d%d",&n,&m)){
        if(n==0 && m==0)break;
        for(int i=1;i<=n;i++)
            head[i]=-1,vis[i]=leaf[i]=0,pre[i].clear();
        cnt=0;
        for(int i=1;i<=m;i++)
            scanf("%d%d",&x,&y),add(x,y),pre[y].push_back(x);
        int h=1;
        vis[h]=1,dfs1(h);//找构造树,叶子节点
        int f=1;
        for(int i=1;i<=n;i++)//看是否连通
            if(!vis[i])
                f=0;
        if(!f){
            printf("No\n");
            continue;
        }
        for(int i=1;i<=n;i++)vis[i]=0;
        vis[h]=1;
        dfs2(h);//反向搜是否根能到所有叶子
        f=0;
        for(int i=1;i<=n;i++)
            if(leaf[i]&&!vis[i])
                f=1;
        if(f)printf("No\n");
        else printf("Yes\n");
    }
    return 0;


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值