题目大意:给定一张有向图,问最少添加几条边使得有向图成为一个强连通图。
跑一边tarjan,如果是联通的就输入0,没有边要加的 , 顺便把图缩点 ;(这类题本博客做过,直接把以前思路贴上来)
要使缩点后的图成为强连通图,每个顶点最少要有一个入度(其他点连接它)和一个出度(它连接其他点),一条边又提供一个出度和一个入度。
所以可以通过统计没有入度的顶点数 noInDegree 和 没有出度的顶点数 noOutDegree。即对于每个缩点,少出度就添,少入度就添;
所需要添加的边数就是noInDegree和noOutDegree中的最大值。
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=30000 ;
const int M=55000 ;
struct node
{
int u,v,next ;
}edge[M] ;
int head[N] ,low[N],dnf[N],stack[N],vis[N] ,belong[N];
int in[N],to[N] ;
int top ,cnt,dep,sum;
void add(int u,int v)
{
edge[top].u=u;
edge[top].v=v;
edge[top].next=head[u] ;
head[u]=top++;
}
void tarjan(int u)
{
int x ;
low[u]=dnf[u]=++dep ;
stack[cnt++]=u ;
vis[u]=1 ;
for(int i=head[u] ;i!=-1;i=edge[i].next)
{
int v=edge[i].v ;
if(!dnf[v])
{
tarjan(v) ;
low[u] = min(low[u],low[v]) ;
}
else if(vis[v])
low[u] = min(low[u],dnf[v]) ;
}
if(low[u]==dnf[u])
{
sum++ ;
do
{
x=stack[--cnt] ;
belong[x] =sum ;
vis[x]=0;
}while(x!=u) ;
}
}
int main()
{
int t,n,m,a,b;
scanf("%d",&t) ;
while(t--)
{
top = 0 ;
cnt=dep=sum = 0 ;
memset(head,-1,sizeof(head)) ;
memset(low,0,sizeof(low)) ;
memset(dnf,0,sizeof(dnf)) ;
memset(vis,0,sizeof(vis)) ;
memset(in,0,sizeof(in)) ;
memset(to,0,sizeof(to)) ;
scanf("%d%d",&n,&m) ;
if(m==0)
printf("%d\n",n) ;
else
{
for(int i = 0 ; i < m ; i++)
{
scanf("%d%d",&a,&b) ;
add(a,b) ;
}
for(int i = 1 ; i <= n ; i++)
{
if(!dnf[i])
tarjan(i) ;
}
for(int i = 1 ; i <= n ; i++)
for(int j=head[i] ;j!=-1;j=edge[j].next)
{
int v= edge[j].v ;
if(belong[i]!=belong[v])
{
in[belong[v]]++ ;
to[belong[i]]++;
}
}
if(sum <= 1 )
printf("0\n") ;
else
{
int sum1=0,sum2=0;
for(int i = 1 ; i <= sum ; i++)
{
if(!in[i]) sum1++;
if(!to[i]) sum2++ ;
}
printf("%d\n",max(sum1,sum2)) ;
}
}
}
return 0;
}