给一张有向图G, 求一个结点数最大的结点集,使得该结点集中任意两个结点u和v满足,要么u可以到达v, 要么v可以到达u(u和v相互可达也可以)。
因为整张图可能存在环路,所以不好使用dp直接做,先采用有向图的强连通分量,进行缩点,然后得到一个有向无环图(DAG) 在采用记忆话dp 去做即可
#include <iostream> #include <cstdio> #include <algorithm> #include <string.h> #include <vector> #include <stack> using namespace std; const int maxn = 1000+10; vector<int>G[maxn]; int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt; stack<int> S; void dfs(int u){ pre[u] = lowlink[u]=++dfs_clock; S.push(u); for(int i=0; i<G[u].size() ; ++i){ int v = G[u][i]; if(!pre[v]){ dfs(v); lowlink[u] = min(lowlink[u], lowlink[v]); }else if(!sccno[v]){ lowlink[u] = min(lowlink[u],pre[v]); } } if(lowlink[u]==pre[u]){ scc_cnt++; for(;;){ int x = S.top(); S.pop(); sccno[x] = scc_cnt; if(x==u)break; } } } void find_scc(int n){ dfs_clock =scc_cnt =0; memset(sccno,0,sizeof(sccno)); memset(pre, 0, sizeof(pre)); while(!S.empty())S.pop(); for(int i=0; i<n; ++i) if(!pre[i]) dfs(i); } int value[maxn],dp[maxn]; vector<int> E[maxn]; int dff(int u){ if(dp[u]!=-1) return dp[u]; dp[u]=0; for(int i=0; i<E[u].size(); ++i){ int v = E[u][i]; dp[u]=max(dff(v),dp[u]); } dp[u]+=value[u]; return dp[u]; } int main() { int cas; scanf("%d",&cas); for(int cc =1; cc<=cas; ++cc){ int n,m; scanf("%d%d",&n,&m); for(int i=0; i<=n; ++i) G[i].clear(),E[i].clear(); for(int i=1; i<=m; ++i){ int u,v; scanf("%d%d",&u,&v); u--; v--; G[u].push_back(v); } find_scc(n); memset(value,0,sizeof(value)); for(int u=0; u<n; ++u){ value[sccno[u]]++; for(int j=0; j<G[u].size(); ++j){ int v=G[u][j]; if(sccno[u]!=sccno[v]){ E[sccno[u]].push_back(sccno[v]); } } } memset(dp , -1 , sizeof(dp)); int ans=0; for(int i=1; i <= scc_cnt; ++i){ if(dp[i]==-1) dff(i); ans=max(ans,dp[i]); } printf("%d ",ans); } return 0; }