【poj2553】The Bottom of a Graph(强连通分量缩点)
题目链接:http://poj.org/problem?id=2553
【题意】
给n个点m条边构成一幅图,求出所有的sink点并按顺序输出。sink点是指该点能到达的点反过来又能回到该点。
【思路】
不难想象sink点一定是在强连通分量中,而且强连通分量缩点后出度为0,就可以说明该强连通分量内所有的点都是sink点。
之前wa了一发是因为写成了out[i],注意是从缩点构成的dag中找出度为0的点,而不是从原来的图中找。
【ac代码】
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <stack>
#include <vector>
#include <cstring>
using namespace std;
const int N = 5005;
int low[N], vis[N], dfn[N], col[N], out[N], ans[N];
vector<int>V[N];
stack<int>s;
int n, cnt, num;
void dfs(int u)
{
s.push(u);
vis[u] = 1;
dfn[u] = low[u] = ++cnt;
for (int i = 0; i < V[u].size(); i++)
{
int v = V[u][i];
if (!dfn[v])
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (vis[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u])
{
int t;
num++;
do
{
t = s.top();
s.pop();
col[t] = num;
vis[t] = 0;
}
while (t != u);
}
}
void tarjan()
{
int i;
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(vis, 0, sizeof(vis));
memset(col, 0, sizeof(col));
while (!s.empty()) s.pop();
cnt = num = 0;
for (i = 1; i <= n; i++)
if (!dfn[i]) dfs(i);
}
int main()
{
int m, i, j;
while(scanf("%d", &n), n)
{
scanf("%d", &m);
for(i = 1; i <= n; i++) V[i].clear();
int a, b;
while(m--)
{
scanf("%d%d", &a, &b);
V[a].push_back(b);
}
tarjan();
memset(out, 0, sizeof out);
for(i = 1; i <= n; i++)
for(j = 0; j < V[i].size(); j++)
{
int v = V[i][j];
if(col[i] != col[v]) out[col[i]]++;//该颜色出度+1
}
cnt = 0;
for(i = 1; i <= n; i++)
if(!out[col[i]]) ans[++cnt] = i;
sort(ans+1, ans+1+cnt);
for(i = 1; i < cnt; i++) printf("%d ", ans[i]);
printf("%d\n", ans[cnt]);
}
return 0;
}