题目大意:在一张无向图中,最大的节点集使得集合内任意两个节点都能到达对方。
题目分析:找出所有的强连通分量,将每一个分量视作大节点,则原图变成了一张DAG。将每个分量中的节点个数作为节点权值,题目便转化为了在DAG中找一条有最大权值和的路径,可以DP解决。
代码如下:
# include<iostream>
# include<cstdio>
# include<vector>
# include<stack>
# include<cstring>
# include<algorithm>
using namespace std;
const int maxn=1005;
int n,m,scc_cnt,dfs_cnt,pre[maxn],low[maxn],sccno[maxn],scc[maxn],dp[maxn];
vector<int>G[maxn],G1[maxn];
stack<int>S;
void read()
{
int a,b;
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i) G[i].clear();
while(m--)
{
scanf("%d%d",&a,&b);
--a,--b;
G[a].push_back(b);
}
}
void dfs(int u)
{
low[u]=pre[u]=++dfs_cnt;
S.push(u);
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(!pre[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}else if(!sccno[v])
low[u]=min(low[u],pre[v]);
}
if(pre[u]==low[u]){
++scc_cnt;
while(1)
{
int x=S.top();
S.pop();
++scc[scc_cnt];
sccno[x]=scc_cnt;
if(x==u)
break;
}
}
}
void findScc()
{
dfs_cnt=scc_cnt=0;
memset(pre,0,sizeof(pre));
memset(low,0,sizeof(low));
memset(scc,0,sizeof(scc));
memset(sccno,0,sizeof(sccno));
for(int i=0;i<n;++i) if(!pre[i])
dfs(i);
}
int DP(int u)
{
if(dp[u]!=-1)
return dp[u];
int res=0;
for(int i=0;i<G1[u].size();++i){
int v=G1[u][i];
res=max(res,DP(v));
}
return dp[u]=scc[u]+res;
}
void solve()
{
for(int i=0;i<=scc_cnt;++i)
G1[i].clear();
vector<int>::iterator it;
for(int u=0;u<n;++u){
for(int i=0;i<G[u].size();++i){
int v=G[u][i];
if(sccno[u]!=sccno[v]){
it=find(G1[sccno[u]].begin(),G1[sccno[u]].end(),sccno[v]);
if(it==G1[sccno[u]].end())
G1[sccno[u]].push_back(sccno[v]);
}
}
}
memset(dp,-1,sizeof(dp));
int ans=0;
for(int i=1;i<=scc_cnt;++i)
ans=max(ans,DP(i));
printf("%d\n",ans);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
read();
findScc();
solve();
}
return 0;
}