Problem B: The Largest Clique
Given a directed graph G, consider the following transformation. First, create a new graphT(G) to have the same vertex set as G. Create a directed edge between two vertices u and v in T(G) if and only if there is a path between u and v in G that follows the directed edges only in the forward direction. This graph T(G) is often called the transitive closure of G.
We define a clique in a directed graph as a set of vertices U such that for any two vertices u and v in U, there is a directed edge either from u to v or from v to u (or both). The size of a clique is the number of vertices in the clique.
The number of cases is given on the first line of input. Each test case describes a graph G. It begins with a line of two integersn and m, where 0 ≤ n ≤ 1000 is the number of vertices of Gand 0 ≤ m ≤ 50,000 is the number of directed edges of G. The vertices of G are numbered from 1 to n. The following m lines contain two distinct integers u and vbetween 1 and n which define a directed edge from u to v in G.
For each test case, output a single integer that is the size of the largest clique in T(G).
Sample input
1 5 5 1 2 2 3 3 1 4 1 5 2
Output for sample input
4
题意:给一张有向图G。求一个节点数最大的节点集,使得该节点集中任意两个节点u和v满足:要么u可以到达v,要么v可以到达u(u和v相互可达也可以)
思路:同一个强连通分量中的点要么都选,要么都不选。把强连通分量收缩点后得到SCC图,让每个SCC节点的权等于他的节点数,则题目转化为求SCC图上权最大的路径。可以用记忆化搜索搞。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 50080
#define maxm 200080
int first[maxn];
int nxt[maxm],vv[maxm];
int dfn[maxn],low[maxn],vis[maxn];
int S[maxn],c[maxn],sum[maxn];
int e,cnt,p,t;
int maxsum[maxn];
int n,m;
void addedge(int u,int v)
{
vv[e] = v; nxt[e] = first[u]; first[u] = e++;
}
void init()
{
e = cnt = p = t = 0;
memset(first,-1,sizeof(first));
memset(dfn,0,sizeof(dfn));
memset(vis,0,sizeof(vis));
memset(sum,0,sizeof(sum));
memset(maxsum,-1,sizeof(maxsum));
}
void Tarjan(int u)
{
vis[u] = 1; S[++p] = u;
dfn[u] = low[u] = ++cnt;
for(int i = first[u];i != -1;i = nxt[i])
{
int v = vv[i];
if(!dfn[v])
{
Tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(vis[v]) low[u] = min(low[u],dfn[v]);
}
if(dfn[u] == low[u])
{
++t;
while(S[p] != u)
{
vis[S[p]] = 0;
c[S[p]] = t;
sum[t]++;
p--;
}
vis[S[p]] = 0; c[S[p]] = t; p--; sum[t]++;
}
}
int dfs(int u)
{
if(maxsum[u] != -1) return maxsum[u];//如果这个强连通分量已经确定了。
maxsum[u] = sum[u];int add = 0;
for(int i = 1;i <= n;i++)
{
if(c[i] == u)//如果这个点是这个强连通分量的
{
for(int j = first[i];j != -1;j = nxt[j])
{
int v = vv[j];
int cv = c[v];
if(u == cv) continue;
add = max(add,dfs(cv));
}
}
}
maxsum[u] += add;
return maxsum[u];
}
int main()
{
//freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
init();
for(int i = 1;i <= m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
if(u == v) continue;
addedge(u,v);
}
for(int i = 1;i <= n;i++)
if(!dfn[i])
{
Tarjan(i);
}
int ans = 0;
for(int i = 1;i <= t;i++)
ans = max(ans,dfs(i));
printf("%d\n",ans);
}
return 0;
}