题目大意:给一个有向图,找出这个图的传递闭包的largest clique。
clique的定义是一个每对结点都有边相连的点集。
原图中的一个强连通分量,一定是传递闭包中的一个clique,因为强连通分量中的每对结点之间都相互可达。
用tarjan算法对原图求出强连通分量后进行缩点,构成了一个DAG(有向无环图)模型,此时问题就转化为了求一条权值最大的路径,每个点的权值即为这个点包含的点的个数,记忆化搜索就好了。
记忆化搜索时用dp[i]表示以结点i为终点的所有路径中的最大权值.
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define V 1005
#define E 50005
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
int m,n,s[V],top,e,ct,scc,ct_scc[V],dfn[V],low[V];
int belong[V],dp[V],indeg[V],outdeg[V],head[V];
vector<int>mp[V];
bool vst[V];
struct Edge
{
int to,next;
}edge[E];
void addedge(int u,int v)
{
edge[e].to=v;
edge[e].next=head[u];
head[u]=e++;
}
void tarjan(int u)
{
int v;
s[top++]=u;
dfn[u]=low[u]=++ct;
vst[u]=1;
int i;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(dfn[v]==0)
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vst[v] && low[u]>dfn[v])
low[u]=dfn[v];
}
if(dfn[u]==low[u])
{
++scc;
do
{
v=s[--top];
vst[v]=0;
belong[v]=scc;
ct_scc[scc]++;
}while(u!=v);
}
}
int d(int u)
{
if(dp[u]!=-1) return dp[u];
int len=mp[u].size();
int i,mx=-1;
for(i=0;i<len;i++)
{
mx=max(mx,d(mp[u][i]));
}
return dp[u]=mx+ct_scc[u];
}
int solve()
{
int u;
M(vst,0);M(dp,-1);M(indeg,0);M(outdeg,0);M(ct_scc,0);M(belong,0);M(dfn,0);M(low,0);
for(u=1;u<=n;u++)
{
if(dfn[u]==0)
tarjan(u);
}
int i;
for(u=1;u<=n;u++)
{
for(i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(belong[u]!=belong[v])
{
mp[belong[v]].push_back(belong[u]);
indeg[belong[v]]++;
outdeg[belong[u]]++;
}
}
}
for(u=1;u<=scc;u++)
{
if(indeg[u]==0) dp[u]=ct_scc[u];
}
int ans=0;
for(u=1;u<=scc;u++) ans=max(ans,d(u));
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--)
{
top=0;
e=0;ct=0;scc=0;
scanf("%d%d",&n,&m);
int i,a,b;
for(i=0;i<=n;i++) mp[i].clear();
M(head,-1);
for(i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
addedge(a,b);
}
printf("%d\n",solve());
}
return 0;
}