【强连通分量+缩点+DAGdp/拓扑排序】UVA11324 The Largest Clique
思路:强连通分量缩点转化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