思路 : 首先用tarjan缩点,那么原图就成了DAG, 判定的条件为 新的DAG图如果只有一个点 (该点可能是孤立的也可能是几个)的出度为0, 那么就将这个点的原图 (可以说是他所包含的原点的集合)中的点的数目表示出来,但是如果出度为0的点个数大于1了,则不成立,直接输出0,因为这样的话就出现两个牛群(牛群可能是一只牛也可能是几只牛),那么就无法做到强连通。
AC代码如下 :
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5; // 1e4会越界
struct node
{
int v, next; //链式前向星
}e[maxn];
int dfn[maxn], low[maxn], sum[maxn], cnt, tot, scnt; // sum记录DAG每个“点”包含原图点的个数
int head[maxn], out[maxn], suo[maxn], n, m; //out表示出度, suo用来存缩点后的新点
bool vis[maxn];
stack <int> st;
void add (int from, int to) {
e[++cnt].v = to;
e[cnt].next = head[from];
head[from] = cnt;
}
void tarjan (int x) {
dfn[x] = low[x] = ++tot;
vis[x] = 1;
st.push(x);
for (int i = head[x]; i != -1; i = e[i].next) {
if (!dfn[e[i].v]) {
tarjan (e[i].v);
low[x] = min (low[x], low[e[i].v]);
}
else if (vis[e[i].v]) low[x] = min (low[x], dfn[e[i].v]);
}
if (dfn[x] == low[x]) {
++scnt; // 新的DAG图的点的个数
int k;
do {
k = st.top();
st.pop();
vis[k] = 0;
sum[scnt]++; //新的DAG图包含的点的个数,每次+1
suo[k] = scnt; // 原图的点若在DAG的一个集合中,那么suo相等
}
while (k != x);
}
}
int main()
{
memset(head, -1, sizeof(head));
cin >> n >> m;
for (int i = 0; i < m; i++) {
int ui, vi;
cin >> ui >> vi;
add (ui, vi);
}
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 = e[j].next) {
int t = e[j].v;
if (suo[i] != suo[t]) out[suo[i]]++; //原来连着,但是新DAG图中不在一起,出度+1
}
}
int ans = 0, oscnt = 0;
for (int i = 1; i <= scnt; i++) {
if (!out[i]) ans = sum[i], oscnt++; // oscnt表示出度为0的个数,
}
if (oscnt > 1) cout << 0 << endl;
else cout << ans << endl;
return 0;
}
事实上,suo数组中存的新DAG点,就是按照tarjan的流程来的