Uva 11324 - The Largest Clique(强连通分量+DP)
题目链接 https://vjudge.net/problem/UVA-11324
【题意】
给定一张无向图G,求一个结点数最大的结点子集,使该子集中任意两结点u,v满足要么从u出发可达v,要么从v出发可达u,互相可达也是可以。
【思路】
大白书323页例题,先找出原图的各个强连通分量,然后把每个强连通分量缩为一点,同时权值为该区域所包含结点的个数,得到一个DAG,那么问题就转换为了求这个DAG上的带权最长路径的问题,用动态规划求解即可,设dp[u]为从u出发时带权路径的最大值,则dp[u]=max{ sccnum[u]+dp[v] | 有向边(u,v) } ,sccnum[u]表示编号为u的强连通分量所包含原来图中结点的个数。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1050;
const int maxm = 50050;
int n, m;
int dfs_clock, scc_cnt;
int dp[maxn];//dp[i]表示以i为起点的最大权值路径对应的权值 dp[u] = max{dp[v]+sccnum[u]|g2边集中的有向边(u,v)}
int pre[maxn], lowlink[maxn], sccno[maxn], sccnum[maxn];//sccnum[i]记录第i个强连通分量所包含的结点数量
vector<int> g[maxn], g2[maxn];
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 (0 == pre[v]) {
dfs(v);
lowlink[u] = min(lowlink[u], lowlink[v]);
}
else if (0 == sccno[v]) {
lowlink[u] = min(lowlink[u], pre[v]);
}
}
if (lowlink[u] == pre[u]) {
++scc_cnt;
sccnum[scc_cnt] = 0;
while (1) {
int x = s.top();
s.pop();
sccno[x] = scc_cnt;
++sccnum[scc_cnt];
if (x == u) break;
}
}
}
void find_scc(int n) {
dfs_clock = scc_cnt = 0;
memset(pre, 0, sizeof(pre));
memset(sccno, 0, sizeof(sccno));
for (int i = 0; i < n; ++i) {
if (0 == pre[i]) dfs(i);
}
}
void init() {
for (int i = 0; i < maxn; ++i) {
g[i].clear();
g2[i].clear();
}
}
int solve(int u) {
if (g2[u].size() == 0) return dp[u] = sccnum[u];
if (-1 != dp[u]) return dp[u];
int ans = 0;
for (int i = 0; i < g2[u].size(); ++i) {
int v = g2[u][i];
ans = max(ans, sccnum[u] + solve(v));
}
return dp[u] = ans;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
init();
scanf("%d%d", &n, &m);
for (int i = 0; i < m; ++i) {
int from, to;
scanf("%d%d", &from, &to);
--from, --to;
g[from].push_back(to);
}
find_scc(n);//求强连通分量
for (int u = 0; u < n; ++u) {
for (int i = 0; i < g[u].size(); ++i) {//建立DAG
int v = g[u][i];
if (sccno[u] != sccno[v]) {
g2[sccno[u]].push_back(sccno[v]);
}
}
}
memset(dp, -1, sizeof(dp));
for (int i = 1; i <= scc_cnt; ++i) {
if (dp[i] == -1) solve(i);
}
int ans = 0;
for (int i = 1; i <= scc_cnt; ++i) {
ans = max(ans, dp[i]);
}
printf("%d\n", ans);
}
return 0;
}