POJ2762(判断无向图的弱连通)

题目:http://poj.org/problem?id=2762

 

题意:给出n个山洞,对于每个山洞,如果任意选择两点s,e,都满足s可以到达e或者e可以到达s,则输出Yes,否则输出No。

 

分析:实际上判断是否弱连通,所以首先强连通,然后缩点,对缩点形成的图最多只能有一个入度为0的点,如果有多个入度为

0的点,则这两个连通分量肯定是不连通的。缩点后形成的图形是一棵树,入度为0的点是这颗树的根,这棵树只能是单链,不

能有分叉,如果有分叉,则这些分叉之间是不可达的,所以就对这棵树进行DFS,如果是单链则是YES。

 

#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
const int N = 10005;

struct Edge
{
    int to;
    Edge *next;
};

Edge *map1[N],*map2[N];  //分别保存原图和缩点后的图
int dfn[N],low[N],stack[N],belong[N],indeg[N];
int index,scc_num,top;
bool tmp[N];
int result[N];

void Tarjan(int u)
{
    dfn[u] = low[u] = ++index;
    stack[++top] = u;
    tmp[u] = true;
    for(Edge *p = map1[u]; p; p = p->next) //枚举每一条边
    {
        int v = p->to;
        if(!dfn[v])
        {
            Tarjan(v);  //dfs继续下找
            low[u] = min(low[u],low[v]);
        }
        else if(tmp[v])
        {
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(low[u] == dfn[u]) //如果节点u是强连通分量的根
    {
        int t;
        ++scc_num;  //强连通分量个数加1
        do
        {
            t = stack[top--];
            tmp[t] = false;
            belong[t] = scc_num;  //记录属于第几个强连通分量
        }
        while(t != u);
    }
}

int Count(int n)
{
    for(int i=1;i<=n;i++)
        if(!dfn[i])
           Tarjan(i);
    return scc_num;
}

int Find() //在新图中找入度为0的点,如果只有一个就返回位置,否则返回0
{
    int record;
    int cnt = 0;
    for(int i=1; i<=scc_num; i++)
    {
        if(indeg[i] == 0)
        {
            cnt++;
            record = i;
        }
    }
    if(cnt == 1) return record;
    return 0;
}

bool TopSort()
{
    int u,num = 0;
    while(u = Find())
    {
        result[num++] = u;
        indeg[u] = -1;
        Edge *p = map2[u];
        while(p)
        {
            indeg[p->to]--;
            p = p->next;
        }
    }
    if(num == scc_num) return true;
    return false;
}

void Init()
{
    index = 0;
    top = 0;
    scc_num = 0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(indeg,0,sizeof(indeg));
    memset(tmp,false,sizeof(tmp));
    memset(map1,NULL,sizeof(map1));
    memset(map2,NULL,sizeof(map2));
    memset(result,0,sizeof(result));
}

int main()
{
    int T,m,n;
    scanf("%d",&T);
    while(T--)
    {
        Init();
        scanf("%d%d",&n,&m);
        while(m--)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            Edge *p = new Edge();
            p->to = b;
            p->next = map1[a];
            map1[a] = p;
        }
        int cnt = Count(n);
        if(cnt == 1)
        {
            puts("Yes");
            continue;
        }
        for(int i=1;i<=n;i++)
        {
            Edge *p = map1[i];
            while(p)
            {
                if(belong[i] != belong[p->to])
                {
                    indeg[belong[p->to]]++;
                    Edge *q = new Edge();
                    q->to = belong[p->to];
                    q->next = map2[belong[i]];
                    map2[belong[i]] = q;
                }
                p = p->next;
            }
        }
        bool flag = false;
        int ans = 0;
        for(int i=1;i<=cnt;i++)
        {
            if(indeg[i] == 0)
               ans++;
        }
        if(ans > 1) flag = false;
        else if(TopSort()) flag = true;
        if(flag) puts("Yes");
        else     puts("No");
    }
    return 0;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值