Description
给出一个竞赛图(每两个点之间都有一条有向边),试找出图中的一个三元环,若不存在输出(-1)。((nle5000))
Solution
注意到竞赛图如果有一个大环,那么中间一定会有三元环。这是比较好证明的。(下面是蒯的)
假设一个竞赛图存在一个(N)元环(大于三元),环上有连续三点(A , B , C)( 存在有向边(AB , BC))
根据竞赛图的定义,一定存在有向边(CA)或(AC)中的一者。
情况(1):若存在(CA),则(A , B , C)构成三元环;
情况(2):若存在(AC) ,不考虑(B)点,剩下的点构成一个((N-1))元环。显然,如果一直不存在情况(1)的话,最终也会形成一个三元环。
这个在纸上自己模拟一下就好了。
所以我们用(Tarjan)找出图中每一个强连通分量。注意到对于中间的某个点对((A,B)),假设它们在原图上的边是(A ightarrow{B}),又因为(B)也可以到(A),所以(A)、(B)一定在同一个大环内。所以我们枚举其中每个点对((A,B)),再钦定某个强连通分量内的点(x),因为(A)、(B)一定在同一个大环内,模拟找三元环的过程,就一定可以找出一个三元环((A,B,x))
Code
#include <bits/stdc++.h>
using namespace std;
int n, t, top, ind, low[5005], dfn[5005], que[5005], vis[5005], rec[5005];
char ch[5005][5005];
int read()
{
int x = 0, fl = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') fl = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();}
return x * fl;
}
void Tarjan(int x)
{
dfn[x] = low[x] = ++ ind;
vis[x] = 1;
que[ ++ top] = x;
for (int y = 1; y <= n; y ++ )
{
if (ch[x][y] != '1') continue;
if (!dfn[y])
{
Tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (vis[y]) low[x] = min(low[x], dfn[y]);
}
if (low[x] == dfn[x])
{
t = 0;
int now = -1;
do
{
now = que[top -- ];
vis[now] = 0;
rec[ ++ t] = now;
} while (now != x);
for (int i = 1; i <= t - 1; i ++ )
{
for (int j = 1; j <= t - 1; j ++ )
{
if (ch[x][rec[i]] == '1' && ch[rec[i]][rec[j]] == '1' && ch[rec[j]][x] == '1' && i != j)
{
printf("%d %d %d
", x, rec[i], rec[j]);
exit(0);
}
}
}
}
return;
}
int main()
{
n = read();
for (int i = 1; i <= n; i ++ )
scanf("%s", ch[i] + 1);
for (int i = 1; i <= n; i ++ )
if (!dfn[i])
Tarjan(i);
puts("-1");
return 0;
}