题意:给出一张有向图,求一个结点数最大的结点集,使得该结点集中随意两个结点u和v满足:要么u能够到到v,要么v能够到达u(u和v能够互相到达)
思路:我们能够缩点,用Tarjan求出全部强连通分量,让每一个SCC的权值等于它的结点个数。因为SCC图是有一个DAG,使用DP求解。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <vector> #include <stack> #include <algorithm> using namespace std; const int MAXN = 1005; vector<int> g[MAXN], scc[MAXN], G[MAXN]; stack<int> s; int pre[MAXN], lowlink[MAXN], sccno[MAXN], sccnum[MAXN], dfs_clock, scc_cnt; int d[MAXN]; int n, m; int Tarjan(int u) { lowlink[u] = pre[u] = ++dfs_clock; s.push(u); for (int i = 0; i < g[u].size(); i++) { int v = g[u][i]; if (!pre[v]) { Tarjan(v); lowlink[u] = min(lowlink[v], lowlink[u]); } 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; sccnum[sccno[x]]++; if (x == u) break; } } } void find_scc() { memset(pre, 0, sizeof(pre)); memset(lowlink, 0, sizeof(lowlink)); memset(sccno, 0, sizeof(sccno)); memset(sccnum, 0, sizeof(sccnum)); dfs_clock = scc_cnt = 0; for (int i = 0; i < n; i++) if (!pre[i]) Tarjan(i); } int dp(int i) { int& ans = d[i]; if (ans > 0) return ans; ans = sccnum[i]; for (int j = 0; j < G[i].size(); j++) { int v = G[i][j]; ans = max(ans, dp(v) + sccnum[i]); } return ans; } int main() { int cas; scanf("%d", &cas); while (cas--) { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) g[i].clear(); int u, v; for (int i = 0; i < m; i++) { scanf("%d%d", &u, &v); u--; v--; g[u].push_back(v); } find_scc(); memset(d, -1, sizeof(d)); memset(G, 0, sizeof(G)); 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]) G[sccno[u]].push_back(sccno[v]); } } int ans = 0; for (int i = 1; i <= scc_cnt; i++) ans = max(ans, dp(i)); printf("%d ", ans); } return 0; }