摘要:
应用Tarjan算法求解缩点后新图的出度为零的点。(套图论的结论)
问题简述:
给定n个点,m条边,用Tarjan算法先缩点,之后求出其他强连通分量都可到达的的强联通分量包含的点的个数。
算法分析:
1. Tarjan算法分析
2. 如何求出其他强连通分量都可以到达的强连通分量
步骤二应用图论的结论:
1、 缩点后的图一定不是强联通图
2 、如果非强连通有向图中存在两个以及两个以上的点出度为0
,则不存在这样的点满足其他点到该点都有边
3 、如果非强连通有向图中有且仅有一个点满足出度为0,则该其他点到该点都有边
代码以及详细注释:
#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>
#pragma warning(disable:4996)
using namespace std;
struct edge {
int to;
int next;
int from;
};
class Solution {
public:
int n, m;
int cnt = 0;
vector<int> head;
vector<edge> e;
vector<int> dfn;
vector<int>low;
vector<bool> visit;
vector<int>sd;
vector<int> sta;
int tp = 0;
int index = 0;
int count = 0; //统计连通分支数量的个数
vector<int> w;
vector<int> visit1;
vector<int> headn;
vector<edge> en;
int cntn = 0;
inline void add_edge(int u, int v) {
++cnt;
e[cnt].from = u;
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void function() {
cin >> n >> m;
dfn.resize(n + 1, 0);
w.resize(n + 1, 1);
low.resize(n + 1, 0);
low.resize(n + 1, 0);
sd.resize(n + 1, 0);
visit.resize(n + 1, false);
head.resize(n + 1, 0);
e.resize(m + 1);
sta.resize(n + 1);
for (int i = 1; i <= m; ++i)
{
int x, y;
cin >> x >> y;
add_edge(x, y);
}
for (int i = 1; i <= n; ++i)
{
if (!dfn[i])
Tarjan(i);
}
//之后每一个点都被缩成一个点
int ans = 0;
vector<int> out(n+1,0);
for (int i = 1; i <= m; ++i)
{
int u = sd[e[i].from];
int v = sd[e[i].to];
if(u!=v)
++out[u];
}
for (int i = 1; i <= n; ++i)
{
if (i==sd[i] && out[sd[i]] == 0 && ans == 0)
{
ans = w[sd[i]];
}
else if (i==sd[i]&&out[sd[i]] == 0 && ans != 0)
ans = 0;
}
cout << ans;
}
void Tarjan(int u) {
dfn[u] = low[u] = ++tp;
sta[++index] = u;
visit[u] = 1;
for (int i = head[u]; i != 0; i = e[i].next)
{
int to = e[i].to;
if (!dfn[to])
{
Tarjan(to);
low[u] = min(low[u], low[to]);
}
else if (visit[to])
{
low[u] = min(low[u], dfn[to]);
}
}
if (dfn[u] == low[u]) {
int y;
while (y = sta[index--]) {
sd[y] = u; //开始缩点
if (y == u)
break;
w[u] += w[y];
}
}
}
};
int main() {
//freopen("in2.txt", "r", stdin);
Solution s;
s.function();
return 0;
}