算法讲解:https://blog.csdn.net/acmmmm/article/details/16361033#comments
例题:
poj-2186
http://poj.org/problem?id=2186
题意:总共有n头牛,m个有序对(a, b) 代表牛a崇拜牛b, 崇拜具有传递性,求出被其他所有牛崇拜的牛的总数。
题解:构造一个图,进行求强连通分量及缩点,在强连通分量中的奶牛肯定是被在这个强连通分量里面奶牛所仰慕的,如果一个图里面有几个强连通分量的话那么一定至少存在一个出度为0的点,如果不存在那么刚刚的强连通一定不是最大强连通又想如果有两个这样的出度为0的点存在,那么这个图中一定没有满足题意的奶牛,因为那两个出度为0的点不会仰慕任何牛所以出度为0的点有且只能有一个!如果有多个那么满足题意的牛只有0个,如果没有的话那么整个图都是满足题意的牛。
ac code:
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<vector>
using namespace std;
#define N 10005
struct eg {
int to, next;
}ac[5*N];
int n, m, cnt, jar;
int head[N], dfn[N], low[N], instack[N], belong[N];
int nu[N];
vector<int>re[N];
stack<int>s;
void init() {
memset(nu, 0, sizeof(nu));
memset(head, -1, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(instack, 0, sizeof(instack));
cnt = 0; jar = 0;
}
void add(int u, int v) {
ac[cnt].to = v;
ac[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt;
s.push(u);
instack[u] = 1;
for (int i = head[u]; i != -1; i = ac[i].next) {
int v = ac[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v])low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
int now;
re[jar].clear();
do {
now = s.top();
s.pop();
instack[now] = 0;
belong[now] = jar;
re[jar].push_back(now);
} while (now != u);
jar++;
}
}
int main() {
int result = 0;
int a, b;
scanf("%d%d", &n, &m);
init();
for (int i = 0; i < m; i++) {
scanf("%d%d", &a, &b);
add(a, b);
}
cnt = 0;
for (int i = 1; i <= n; i++) {
if (dfn[i] == 0)
tarjan(i);
}
for (int i = 1; i <= n; i++) {
for (int j = head[i]; j != -1; j = ac[j].next) {
int v = ac[j].to;
if (belong[i] != belong[v]) {
nu[belong[i]] = 1;
}
}
}
int ans = 0, t;
for (int i = 0; i < jar; i++) {
if (!nu[i]) {
ans++;
t = i;
}
}
if (ans > 1) {
cout << result << endl;
return 0;
}
cout << re[t].size() << endl;
return 0;
}
poj-2553
http://poj.org/problem?id=2553
求从u出发能到v,并且从v出发也能到u的这些点,并且将其输出;求出一个图的若干个强连通分量并且如果这个强连通分量中的缩点出度为0的话那么这个强连通分量里面的点就都满足题意。
ac code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
#define N 5005
int n, e, cnt, ans;
int head[N], dfn[N], low[N], belong[N], nu[N], instack[N], vis[N];
vector<int>re[N];
struct eg {
int to, next;
}a[10*N];
stack<int>s;
void init() {
ans = cnt = 0;
memset(head, -1, sizeof(head));
memset(nu, 0, sizeof(nu));
memset(dfn, 0, sizeof(dfn));
memset(instack, 0, sizeof(instack));
memset(vis, 0, sizeof(vis));
}
void add(int u, int v) {
a[cnt].to = v;
a[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt;
s.push(u);
instack[u] = 1;
for (int i = head[u]; i != -1; i = a[i].next) {
int v = a[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v])low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
int now;
re[ans].clear();
do {
now = s.top();
s.pop();
belong[now] = ans;
re[ans].push_back(now);
instack[now] = 0;
} while (now != u);
ans++;
}
}
int main() {
int x, y;
while (~scanf("%d", &n) && n != 0) {
scanf("%d", &e);
init();
for (int i = 0; i < e; i++) {
scanf("%d%d", &x, &y);
add(x, y);
}
cnt = 0;
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
tarjan(i);
}
}
for (int i = 1; i <= n; i++) {
for (int j = head[i]; j != -1; j = a[j].next) {
int v = a[j].to;
if (belong[v] != belong[i]) {
nu[belong[i]] = 1;
}
}
}
for (int i = 0; i < ans; i++) {
if (!nu[i]) {
for (int j = 0; j < re[i].size(); j++) {
vis[re[i][j]] = 1;
}
}
}
int res;
for (int i = 1; i <= n; i++) {
if (vis[i]) {
res = i;
cout << i;
break;
}
}
for (int i = res + 1; i <= n; i++)
if (vis[i])
cout << " " << i;
cout << endl;
}
return 0;
}
poj-2762
http://poj.org/problem?id=2762
题意:一个有向图中有n个点m条边,问是否任意两点u,v之间可以u-v或者v->u。
思路:学过强连通可以知道,在一个强连通中任意两点都可以满足题目条件。如果图中有多个强连通分量,则需要在强连通分量之间建图,再拓扑排序判断图中是否同时存在一个入度为零的两点,如果存在那么这两点肯定不满足题目条件。
ac code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<vector>
using namespace std;
#define N 1005
int n, m, cnt, ans, t;
int head[N], dfn[N], low[N], belong[N], nu[N], instack[N], vis[N];
vector<int>re[N];
struct eg {
int to, next;
}a[10 * N];
stack<int>s;
void init() {
memset(head, -1, sizeof(head));
memset(dfn, 0, sizeof(dfn));
memset(instack, 0, sizeof(instack));
memset(nu, 0, sizeof(nu));
cnt = ans = 0;
}
void add(int u, int v) {
a[cnt].to = v;
a[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt;
s.push(u);
instack[u] = 1;
for (int i = head[u]; i != -1; i = a[i].next) {
int v = a[i].to;
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v])low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
int now;
re[ans].clear();
do {
now = s.top();
s.pop();
re[ans].push_back(now);
instack[now] = 0;
belong[now] = ans;
} while (now != u);
ans++;
}
}
void solve() {
set<int>st[N];
vector<int>h;
int flag = 0;
for (int i = 1; i <= n; i++) {
for (int j = head[i]; j != -1; j = a[j].next) {
int v = a[j].to;
if (belong[v] != belong[i]) {
st[belong[i]].insert(belong[v]);
}
}
}
for (int i = 0; i < ans; i++) {
for (set<int>::iterator ite = st[i].begin(); ite != st[i].end(); ite++) {
//cout << i << " " << *ite << endl;
nu[*ite]++;
}
}
for (int i = 0; i < ans; i++) {
if (!nu[i]) {
h.push_back(i);
}
}
while (1) {
if (h.size() == 0)
break;
if (h.size() > 1) {
flag = 1;
cout << "No" << endl;
break;
}
int t = h[0];
h.clear();
for (set<int>::iterator ite = st[t].begin(); ite != st[t].end(); ite++) {
nu[*ite]--;
//cout << *ite << " " << nu[*ite] << endl;
if (nu[*ite] == 0)
h.push_back(*ite);
}
}
if (!flag)
cout << "Yes" << endl;
}
int main() {
int u, v;
cin >> t;
while (t--) {
scanf("%d%d", &n, &m);
init();
for (int i = 0; i < m; i++) {
scanf("%d%d", &u, &v);
add(u, v);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
cnt = 0;
tarjan(i);
}
}
if (ans == 1) {
cout << "Yes" << endl;
}
else {
solve();
}
}
return 0;
}