题目链接: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 "); else printf("No "); } return 0; }