【強連通分量+縮點+DAGdp/拓撲排序】UVA11324 The Largest Clique
阿新 • • 發佈:2020-10-14
思路:強連通分量縮點轉化DAG+DAG上的DP
解法一:記憶化搜尋
#define mem(a,n) memset(a,n,sizeof(a)) #define f(i,a,b) for(int i=a;i<=b;i++) #define af(i,a,b) for(int i=a,i>=b;i--) using namespace std; typedef long long LL; const int INF = 20010509; const int maxn = 1e3 + 100; const int maxm = 5e4 + 100; stack<int> s; int dfs_clock, scc_cnt; int dfn[maxn], low[maxn], sccno[maxn]; int head[maxn], headnew[maxn], cnt = 0; int n, m; int val[maxn], dp[maxn]; struct Edge { int next, to; }e[maxm], enew[maxm]; void add(int from, int to, Edge eset[], int head[]) { cnt++; eset[cnt].next = head[from]; eset[cnt].to = to; head[from] = cnt; } void dfs(int u) { dfn[u] = low[u] = ++dfs_clock; s.push(u); for (int i = head[u]; i; i = e[i].next) { int v = e[i].to; if (!dfn[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if (!sccno[v]) { low[u] = min(low[u], dfn[v]); } } if (low[u] == dfn[u]) { scc_cnt++; while (1) { int x = s.top(); s.pop(); sccno[x] = scc_cnt; val[scc_cnt]++; if (x == u) break; } } } void find_scc(int n) { while (!s.empty()) s.pop(); dfs_clock = scc_cnt = 0; mem(sccno, 0); mem(dfn, 0); mem(low, 0); for (int i = 1; i <= n; i++) { if (!dfn[i]) dfs(i); } } int Dp(int i) { //實際上是記憶化搜尋 if (dp[i]) return dp[i]; dp[i] = val[i]; for (int j = headnew[i]; j; j = enew[j].next) { int v = enew[j].to; dp[i] = max(dp[i], Dp(v) + val[i]); } return dp[i]; } void init() { cnt = 0; mem(e, 0); mem(enew, 0); mem(headnew, 0); mem(head, 0); mem(val, 0); mem(dp, 0); } int main(){ int t; cin >> t; while (t--) { init(); cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; add(u, v, e, head); } find_scc(n); cnt = 0; //將SCC都縮成一個點,建立新圖 for (int u = 1; u <= n; u++) { for (int i = head[u]; i; i = e[i].next) { int v = e[i].to; if (sccno[u] != sccno[v]) { add(sccno[u], sccno[v], enew, headnew); } } } int ans = 0; for (int i = 1; i <= scc_cnt; i++) { ans = max(ans, Dp(i)); } cout << ans << endl; } return 0; }
解法二:拓撲排序+DP