题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2299
题目大意:有n个人,m条单向边,现在让你找出人数尽量多的一个集合,使得其中任意两个点至少存在一条路径。输出这个最大的人数。
解题思路:很明显,对于一个SCC,人数要多,肯定是都选。所以我们先缩点,得到另外一张SCC图,是一个DAG。把每个SCC的人数当做权值,然后dp找最长路即可。
代码如下:
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int MAXN = 1111;
vector <int> G[MAXN],G2[MAXN];
int low[MAXN],pre[MAXN],sccno[MAXN];
int dfs_clock,scc_cnt;
stack <int> S;
int num[MAXN];
void dfs(int u)
{
low[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])
{
dfs(v);
low[u] = min(low[u],low[v]);
}
else if(!sccno[v])
{
low[u] = min(low[u],pre[v]);
}
}
if(low[u] == pre[u])
{
++scc_cnt;
num[scc_cnt] = 0;
for(;;)
{
int x = S.top();
S.pop();
sccno[x] = scc_cnt;
num[scc_cnt]++;
if(x == u) break;
}
}
}
void find_scc(int n)
{
memset(pre,0,sizeof(pre));
memset(sccno,0,sizeof(sccno));
dfs_clock = scc_cnt = 0;
for(int i = 0;i < n;i++)
if(!pre[i]) dfs(i);
}
int vis[MAXN];
int d[MAXN];
void dp(int x)
{
if(vis[x]) return;
vis[x] = 1;
d[x] = num[x];
for(int i = 0;i < G2[x].size();i++)
{
int v = G2[x][i];
dp(v);
d[x] = max(d[x],d[v]+num[x]);
}
}
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i = 0;i < n;i++)
G[i].clear();
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
a--,b--;
G[a].push_back(b);
}
find_scc(n);
for(int i = 1;i <= scc_cnt;i++)
G2[i].clear();
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])
G2[sccno[u]].push_back(sccno[v]);
}
memset(vis,0,sizeof(vis));
int ans = 0;
for(int i = 1;i <= scc_cnt;i++)
{
dp(i);
ans = max(d[i],ans);
}
printf("%d\n",ans);
}
return 0;
}