题目大意:
有若干个点,每个点之间有无向边或者有向边,并且只能走一次。问有没有存在一个点能够走出去又走回来的。
思路:
这道题思路当时一下子就想出来了。
从每一个联通块的任何一个点出发去dfs,搜到一个点,将其标记,并继续往下搜,假如这个点已经被标记过了,那么说明之前从这个点出发过,并且现在又可以回到这个点。
结束!
现在说一下处理边只走一次的方法:我是用奇数表示一个方向,偶数表示这个边的另外一个方向。无论走到哪一条边,对其下标取异或就能够得到另外一个方向的那条边。标记,这样就可以了。
#pragma comment(linker, "/STACK:102400000,102400000")
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 1100000
int head[MAX];
int cnt;
int visn[MAX];
int vis[MAX << 2];
bool flag;
int m1, m2;
struct edge
{
int v;
int next;
}edg[MAX<<2];
void init()
{
cnt = 0;
memset(head, -1, sizeof head);
memset(vis, 0, sizeof vis);
memset(visn, 0, sizeof (visn));
}
void addedge(int u, int v,bool tg)
{
edg[cnt].v = v;
edg[cnt].next = head[u];
head[u] = cnt++;
if (tg)
{
swap(u, v);
edg[cnt].v = v;
edg[cnt].next = head[u];
head[u] = cnt++;
}
}
void dfs(int u)
{
if (flag)
return;
for (int i = head[u]; i != -1; i = edg[i].next)
{
if (!vis[i])
{
int v = edg[i].v;
if (visn[v])
{
flag = true;
return;
}
visn[v] = 1;
vis[i] = 1;
if (i <= (m1 << 1))
vis[i ^ 1] = 1;
dfs(v);
visn[v] = 0;
vis[i] = 0;
if (i <= (m1 << 1))
vis[i ^1] = 0;
}
}
}
int main()
{
int t;
cin >> t;
while (t--)
{
init();
int n;
scanf("%d%d%d", &n, &m1, &m2);
int u, v;
for (int i = 0; i < m1; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v, 1);
}
for (int i = 0; i < m2; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v,0);
}
flag = false;
for (int i = 1; i <= n; i++)
{
if (flag)
break;
visn[i] = 1;
dfs(i);
visn[i] = 0;
}
if (flag)
puts("YES");
else
puts("NO");
}
}