POJ 2762 Going from u to v or from v to u?【强连通Kosaraju+拓扑排序】

Going from u to v or from v to u?

Time Limit: 2000MS

 

Memory Limit: 65536K

Total Submissions: 16457

 

Accepted: 4382

Description

In order to make their sons brave, Jiajia and Wind take them to a big cave. The cave has n rooms, and one-way corridors connecting some rooms. Each time, Wind choose two rooms x and y, and ask one of their little sons go from one to the other. The son can either go from x to y, or from y to x. Wind promised that her tasks are all possible, but she actually doesn't know how to decide if a task is possible. To make her life easier, Jiajia decided to choose a cave in which every pair of rooms is a possible task. Given a cave, can you tell Jiajia whether Wind can randomly choose two rooms without worrying about anything?

Input

The first line contains a single integer T, the number of test cases. And followed T cases. 

The first line for each case contains two integers n, m(0 < n < 1001,m < 6000), the number of rooms and corridors in the cave. The next m lines each contains two integers u and v, indicating that there is a corridor connecting room u and room v directly. 

Output

The output should contain T lines. Write 'Yes' if the cave has the property stated above, or 'No' otherwise.

Sample Input

1

3 3

1 2

2 3

3 1

Sample Output

Yes

Source

POJ Monthly--2006.02.26,zgl & twb

 

题目大意:给你n个点,m条边,这个图是否能够任取两点u,v满足一定有路径从u能够到达v,或者是从v有路径能够到达u。如果满足,输出Yes,否则输出No


思路:

1、现在假设所有图都是DAG图(有向无环图)

我们先观察一个输出Yes的图:


通过观察可以得知,无论选择哪两个点,都满足有一条路径能够从u到v或者从v到u。

如果我们这样考虑,因为入度为0的点如果被选中两点之一,无疑这个点一定要作为起点出发,找一条路能够到达另一个点。如果我们模拟出这个过程,用拓扑排序的方法最好不过了,如果能够拓扑出目标点,那么就能说明从度为0的点能够到达目的点,那么拓扑序中的点也都是满足这一条件的,比如图中的拓扑序:

1 2 3 4 5,我们选其中的任意两个,都能有一条有向路径,从左到右的经过一些点之后到达目的地,这时候就不用考虑到底从u到v还是从v到u的问题了。

我们刚刚讨论的是任意两点是否能够弱连通可以用拓扑排序模拟寻路过程,来判断是否能从一个点到达另一个点,那么如何判断是否能够满足任意两点都行呢?

我们再观察一个输出No的图:


其拓扑过程显然和上图有个明显的差别,拆点1之后,一下子就拓扑出来两个度为0的节点,2和5,那么不用想,2和5之间是一定没有有向边相通的,因为2和5都是能够从1走过来,但是他们之间却没有任何的依赖关系,所以,这种情况,也就是要输出No的情况。


综上所述,如果在拓扑排序的过程中,一下子拓扑出来两个点及以上个数的点的话,也就是要输出No的情况,因为这两个或者两个以上的点之间没有依赖关系,所以他们之间一定没有边能够使得他们想通。


2、刚刚我们讨论在DAG图下的Yes、No判断,那么我们如何得到DAG图呢?我们使用Kosaraju算法求强连通分量的同时,对一个强连通分量染色(当然是染成一个颜色),然后缩点,得到DAG图、然后对DAG图进行拓扑排序的判断,得到结果。


AC代码:

#include<stdio.h>
#include<vector>
#include<string.h>
#include<queue>
using namespace std;
vector<int >mp[1001];
vector<int >mpp[1001];
vector<int >map[1001];
int degree[1001];
int color[1001];
int num[1001];
int vis[1001];
int n,m,sig;
int top(int n)
{
    queue<int >s;
    int judge=0;
    int tot=0;
    for(int i=1;i<=n;i++)
    {
        if(degree[i]==0)
        {
            tot++;
            degree[i]=-1;
            s.push(i);
        }
    }
    if(tot>1)return 0;
    while(!s.empty())
    {
        int u=s.front();
        s.pop();
        tot=0;
        for(int j=0;j<map[u].size();j++)
        {
            int v=map[u][j];
            degree[v]--;
            if(degree[v]==0)
            {
                degree[v]=-1;
                tot++;
                s.push(v);
            }
        }
        if(tot>1)return 0;
    }
    return 1;
}
void Dfs(int u)
{
    vis[u]=1;
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(vis[v]==0)
        {
            Dfs(v);
        }
    }
    num[sig++]=u;
}
void Dfs2(int u)
{
    vis[u]=1;
    color[u]=sig;
    for(int i=0;i<mpp[u].size();i++)
    {
        int v=mpp[u][i];
        if(vis[v]==0)
        {
            Dfs2(v);
        }
    }
}
void Kosaraju()
{
    sig=1;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++)
    {
        if(vis[i]==0)
        {
            Dfs(i);
        }
    }
    sig=0;
    memset(vis,0,sizeof(vis));
    for(int i=n;i>=1;i--)
    {
        if(vis[num[i]]==0)
        {
            sig++;
            Dfs2(num[i]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<mp[i].size();j++)
        {
            int v=mp[i][j];
            if(color[i]!=color[v])
            {
                map[color[i]].push_back(color[v]);
                degree[color[v]]++;
            }
        }
    }
    int tt=top(sig);
    if(tt==0)
    {
        printf("No\n");
    }
    else printf("Yes\n");
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(degree,0,sizeof(degree));
        for(int i=0;i<=n;i++)map[i].clear();
        for(int i=0;i<=n;i++)mp[i].clear();
        for(int i=0;i<=n;i++)mpp[i].clear();
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            mp[x].push_back(y);
            mpp[y].push_back(x);
        }
        Kosaraju();
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值