题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2767
分析:首先找出强连通分量,然后把每个强连通分量缩成一个点,得到一个DAG。接下来,设有a个结点(别忘了,这里的每个结点对应于原图的一个强连通分量)的入度为0,b个结点的出度为0,则max{a,b}就是答案。注意特殊情况:当原图已经强连通时,答案是0而不是1。
代码如下:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <stdlib.h>
#include <vector>
#include <stack>
using namespace std;
const int maxn =20000+5;
int T,n,m;
int in[maxn],out[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;///scc_cnt有多少个连通分量 lowlink能追溯到的最早祖先
/// sccno[i]=j表示i节点属于j连通分量
vector<int>G[maxn];
stack<int>S;
void dfs(int u)
{
pre[u] = lowlink[u] = ++dfs_clock;
S.push(u);
for(int i=0; i<G[u].size(); i++)
{
int v = G[u][i];
if(!pre[v])
{
dfs(v);
lowlink[u] = min(lowlink[u],lowlink[v]);
}
else if(!sccno[v])
{
lowlink[u] = min(lowlink[u], pre[v]);
}
}
if(lowlink[u]==pre[u])
{
scc_cnt++;
for(;;)
{
int x = S.top();
S.pop();
sccno[x] = scc_cnt;
if(x==u)
break;
}
}
}
void find_scc(int n)
{
dfs_clock = scc_cnt =0;
memset(sccno, 0 , sizeof(sccno));
memset(pre, 0, sizeof(pre));
for(int i=0; i<n; i++)
if(!pre[i])
dfs(i);
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
G[i].clear();
for(int i=0; i<n; i++)
G[i].clear();
for(int i=0; i<m; i++)
{
int u,v;
scanf("%d%d",&u,&v);
u--;v--;
G[u].push_back(v);
}
find_scc(n);
for(int i=1; i<=scc_cnt; i++)
in[i] = out[i] = 1;
for(int u=0; u<n; u++)
for(int i=0; i<G[u].size(); i++)
{
int v = G[u][i];
if(sccno[u]!=sccno[v])
in[sccno[v]] = out[sccno[u]] = 0;
}
int a=0,b=0;
for(int i=1; i<=scc_cnt; i++)
{
if(in[i])
a++;
if(out[i])
b++;
}
int ans = max(a,b);
if(scc_cnt==1)
ans=0;
printf("%d\n",ans);
}
return 0;
}