0%

The Largest Clique

%% ### 题目描述

给你一张有向图 \(G\),求一个结点数最大的结点集,使得该结点集中的任意两个结点 \(u\)\(v\) 满足:要么 \(u\) 可以达 \(v\),要么 \(v\) 可以达 \(u\)\(u,v\) 相互可达也行)。

输入格式

第一行,输入一个整数,代表测试数据组数 \(T\),每组数据的格式如下。

第一行为结点数 \(n\) 和边数 \(m\),结点编号 \(1 \sim n\)

以下 \(m\) 行每行两个整数 \(u\)\(v\) ,表示一条有向边 \(u \to v\)

输出格式

对于每组数据,输出最大结点集的结点数。 %%

原题 PDF

Description

给定一个有向图 \(G\),考虑以下转换。 首先,创建一个新图 \(T (G)\),其顶点集与 \(G\) 相同。如果 \(G\) 中存在从 \(u\)\(v\) 的路径只按照正向方向连通,则在 \(T (G)\)中创建从顶点 \(u\) 到顶点 \(v\) 的有向边。这个图 \(T(G)\)通常被称为 \(G\) 的传递闭包。 我们定义有向图中的团为顶点集 \(U\),使得对于 \(U\) 中的任意两个顶点 \(u\)\(v\),从 \(u\)\(v\) 或者从 \(v\)\(u\)(或者两者都有)存在有向边。团的大小是指团中顶点的数量。

Input

输入的第一行给出了案例的数量。 每个测试案例描述了一个图 \(G\)。 它以两个整数 \(n\)\(m\) 开头,其中 G 的顶点数 (\(0≤ n ≤ 1000\)), G 的有向边的数量 \((0≤ m ≤ 50,000)\)\(G\) 的顶点从 \(1\)\(n\) 编号。 接下来的 \(m\) 行包含两个不同的整数 \(u\)\(v\),它们在 \(1\)\(n\) 之间,定义了 \(G\) 中从 \(u\)\(v\) 的有向边。

Output

对于每个测试案例,输出一个整数,表示 \(T (G)\) 中最大团的大小。

Sample Input

1
2
3
4
5
6
7
1
5 5
1 2
2 3
3 1
4 1
5 2

Sample Output

1
4

代码

Tarjan+记忆化搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
vector<int> e[N],e2[N];
int n, m, t, dfn[N], low[N], instk[N], siz[N], scc[N], stk[N], top, tot, cnt, in[N], out[N], maxsum[N];

void tarjan(int x)
{
dfn[x] = low[x] = ++tot;
stk[++top] = x;
instk[x] = 1;

for (int y : e[x])
{ //邻接表存图
if (!dfn[y])
{
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (instk[y])
{
low[x] = min(low[x], low[y]);
}
}

if (dfn[x] == low[x])
{
int y;
++cnt;
do
{
y = stk[top--];
instk[y] = 0;
scc[y] = cnt;
++siz[cnt];
} while (y != x);
}
}
void dfs(int u)
{
if (maxsum[u] != -1)
return;

maxsum[u] = siz[u];
for (int v : e2[u])
{
dfs(v);
maxsum[u] = max(maxsum[u], siz[u] + maxsum[v]);
}
}
void solve()
{
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(scc, 0, sizeof scc);
memset(siz, 0, sizeof siz);
memset(instk, 0, sizeof instk);
memset(stk, 0, sizeof stk);
memset(in, 0, sizeof in);
memset(out, 0, sizeof out);
memset(maxsum, -1, sizeof maxsum);
for (int i = 1; i <= n; i++)
e[i].clear(),e2[i].clear();
cnt = 0, tot = 0, top = 0;
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int a, b;
cin >> a >> b;
e[a].push_back(b);
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);

for (int i = 1; i <= n; i++)//重新构建缩点之后的图
{
for (int j : e[i])
{
if (scc[i] != scc[j])
{
e2[scc[i]].push_back(scc[j]);
in[scc[j]]++;
out[scc[i]]++;
}
}
}
int ans = 0;
for (int i = 1; i <= cnt; i++)
{
if (!in[i])
{
dfs(i);
ans = max(ans, maxsum[i]);
}
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> t;
while (t--)
solve();
}

  • 本文作者: FXJFXJ
  • 本文链接: https://fxj.wiki/posts/1fc66dad/
  • 版权声明: 本博客所有文章除特别声明外,均采用 ZERO 许可协议。转载请注明出处!