tarjan算法,即dfs找low和dfn,用timmer表示dfs的时间(经历各个点的次序)
开始不明白怎么进行缩点,后来发现就是染色,同一个颜色的点如果有连接到其他颜色的点就算出度不为0
如果出度为0的点(染色后)就一个,即为这个连通分量的所有点
如果有很多个,说明不存在被所有喜欢的牛(两个出度为0的点不可能相互联系),为0
#include<iostream>
#include<algorithm>
#include<set>
#include<stack>
#include<vector>
#include<string.h>
#include<string>
using namespace std;
const int maxn1 = 10005;
const int maxn2 = 50005;
vector<int>G[maxn2];
stack<int>S;
int N, M, color_num;
int out[maxn1];
int color[maxn1],timeer,dfn[maxn1],low[maxn1];
int zerototal;//出度为0的缩点的个数
void dfs(int u)
{
S.push(u);
low[u] = dfn[u] = timeer++;
for (int i = 0; i < G[u].size(); i++)
{
int v= G[u][i];//下一个边
if (!dfn[v])//是访问而不是判栈中
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else
{
low[u] = min(low[u], low[v]);
}
}
if (dfn[u] == low[u])
{
int v = 0;
color_num++;
while (v != u)
{
v = S.top();
S.pop();
color[v] = color_num;//染色类似缩点
}
}
}
void cal()
{
for (int i = 1; i <= N; i++)
{
if (out[color[i]])
{
continue;
}
for (int j = 0; j < G[i].size(); j++)
{
int v = G[i][j];//出结点
if (color[v] != color[i])//如果是连别的颜色
{
out[color[i]] = true;
}
}
}
int tmp;
for (int i = 1; i <= color_num; i++)//缩小之后的点数
{
if (!out[i])//如果是练的自己
{
zerototal++;
tmp = i;//当前的出度为0的缩小之后的点
}
}
if (zerototal == 1)
{
int ans = 0;
for (int i = 1; i <= N; i++)
{
if (color[i] == tmp)
{
ans++;
}
}
cout << ans << endl;
}
else
{
printf("0\n");
}
}
int main()
{
timeer = 1;
color_num = 0;
zerototal = 0;
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(out, 0, sizeof(out));
cin >> N >> M;
while (M--)
{
int be, ed;
cin >> be >> ed;
G[be].push_back(ed);
}
for (int i = 1; i <= N; i++)
{
if (!dfn[i])
{
dfs(i);
}
}
cal();
return 0;
}