题目大意:在一张无向图中,最大的节点集使得集合内任意两个节点都能到达对方。
题目分析:找出所有的强连通分量,将每一个分量视作大节点,则原图变成了一张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 ",ans); } int main() { int T; scanf("%d",&T); while(T--) { read(); findScc(); solve(); } return 0; }