题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5222
题意:有一个探险家,探险一个地方,这个地方有n个洞穴,有若干条边,有的边是有向边,有的是无向边,每条边在走过后就坍塌,问你这个探险家能不能选择一个点,然后经过最少一个点,然后回到原点
解析:对于所有的无向边做并查集,如果对于两个点,在未合并前就已经是一个集合了,那么就存在环,即有解,若果此时没有判断出环的话,那么仅仅只靠无向边是找不到的,那么可以把合并后的点生成的一个新的图,于是就相当于判断这个新生成的无向图是否存在环,此时用拓扑排序判断即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
vector<int>G[maxn];
int in[maxn],fa[maxn];
void init(int n)
{
for(int i=0;i<=n;i++)
{
in[i] = 0,fa[i] = i;
G[i].clear();
}
}
int getfa(int x)
{
if(x==fa[x])
return x;
return fa[x] = getfa(fa[x]);
}
bool judge(int n)
{
int cnt = 0;
queue<int>q;
for(int i=1;i<=n;i++)
{
if(in[i]==0)
q.push(i);
}
while(!q.empty())
{
int u = q.front();q.pop();
cnt++;
for(int i=0;i<(int)G[u].size();i++)
{
int v = G[u][i];
in[v]--;
if(in[v]==0)
q.push(v);
}
}
return cnt!=n;
}
int main(void)
{
int t,n,m1,m2;
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d",&n,&m1,&m2);
int x,y,t1,t2,flag = 0;
init(n);
for(int i=0;i<m1;i++)
{
scanf("%d %d",&x,&y);
t1 = getfa(x);
t2 = getfa(y);
if(t1==t2)
flag = 1;
else
fa[t1] = t2;
}
for(int i=0;i<m2;i++)
{
scanf("%d %d",&x,&y);
t1 = getfa(x);
t2 = getfa(y);
if(t1==t2)
flag = 1;
else if(!flag)
{
G[t1].push_back(t2);
in[t2]++;
}
}
if(flag || judge(n))
puts("YES");
else
puts("NO");
}
return 0;
}