poj 2762 Going from u to v or from v to u?(tarjan缩点+拓扑排序)

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

题目意思:一个山洞有n个房间,m条单向边,要任意两个房间(假如房间x,y),要x可以到y,或者y到x(或者不是并且)

如果可以就输出Yes,不是就输出No。

 

思路:题目是或者不是并且,并且的话,直接求是否是强联通图即可。

或者的话,我们可以这样想本质上是求该图是否为数据结构中单向连通图

因为可能有环所以要

1.先用tarjan缩点变成一个DAG(有向无环图)。

2.再利用拓扑排序的运用,只要每次找入度为0的点时,点的数量不超过一个。

为什么?我们可以这样想,缩完点的图可以看作一棵树,子树(即入度为0)不可以有两个,因为他们不可以相互到达。

 

我想的时候有个错误的想法:(缩完点)不是不能有两个子树吗?那我求每个点的出度,出度为1的点数是点数减一不就是答案吗?还要缩什么点?

它是个图不是树,说成树是好理解。(缩完点的图)反例:出度为1的点只有1个,但这是一个单向连通图。

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e3+100;
struct node{
    int next,to;
}edge[maxn*maxn];
int head[maxn],low[maxn],dfn[maxn],belong[maxn],visit[maxn];
int in[maxn],st[maxn],n,m,cnt,tot,top,num;
vector<int> ve[maxn];//记录缩点之后的连接关系 
queue<int> q;
void init()//初始化 
{
    while(!q.empty())
        q.pop();
    memset(head,-1,sizeof(head));
    memset(visit,0,sizeof(visit));//标记是否在栈内 
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low)); 
    memset(belong,0,sizeof(belong));//记录每个点所属于的缩点编号 
    memset(in,0,sizeof(in));//记录每个缩点的入度 
    memset(st,0,sizeof(st));//st模拟栈 
    cnt=tot=top=num=0;
}

void add(int u,int v)//前向星连边 
{    
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void tarjan(int u)//tarjan缩点模板 
{
    dfn[u]=low[u]=++tot;
    visit[u]=1;
    st[++top]=u;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(visit[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        int t;
        num++;//计算缩点的数量 
        do{
            t=st[top--];
            visit[t]=0;
            belong[t]=num;////记录这个点所属于的缩点编号
        }while(t!=u);
    }
}

bool solve()
{
    for(int i=0;i<=maxn;i++) 
        ve[i].clear();
    for(int i=1;i<=n;i++)//tarjan缩点 
        if(!dfn[i])
            tarjan(i);
    for(int u=1;u<=n;u++)//寻找每个缩点的关系 
    {
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(belong[u]!=belong[v])//不在一个缩点,那么两个点相连 
            {
                in[belong[v]]++;//入度 
                ve[belong[u]].push_back(belong[v]);//记录边的关系 
            }
        }
    }
    //拓扑排序 
    for(int i=1;i<=num;i++)
        if(!in[i])//入度为0的入队列 
            q.push(i);
    if(q.size()>1)//入度为0的个数 
        return false;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<ve[u].size();i++)
        {
            int v=ve[u][i];
            in[v]--;
            if(!in[v])
                q.push(v);
        }
        if(q.size()>1)//入度为0的个数 不能同时大于1 
            return false;
    }    
    return true;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&n,&m);
        int u,v;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        if(solve())
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

 

posted @ 2018-11-23 19:07  怀揣少年梦.#  阅读(146)  评论(0编辑  收藏  举报