题意:已经有条有向边,求最少再有多少条有向边,可以使任意两点互通。
做法:强连通缩点,求出缩点后所有入度为零的个数,和所有出度为零的点的个数,选其中比较大的那个。
注意:当缩点后为一个点的时候,直接判断为零
#include <iostream>
#include <string.h>
#include <vector>
#include <stack>
using namespace std;
vector<int>map[20005];
stack<int>test;
int sign[20005],low[20005],dfn[20005],belong[20005],loop[20005],in[20005],out[20005],time,count;
int min(int a,int b)
{
return a<b?a:b;
}
void init()
{
int i;
memset(loop,0,sizeof(loop));
memset(sign,0,sizeof(sign));
memset(belong,0,sizeof(belong));
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
}
void tarjan(int a)
{
loop[a]=1;
int i;
test.push(a);
dfn[a]=low[a]=time++;
int sb=map[a].size();
for(i=0;i<sb;i++)
{
if(!loop[map[a][i]])
{
tarjan(map[a][i]);
low[a]=min(low[a],low[map[a][i]]);
}
else if(loop[map[a][i]]&&!sign[map[a][i]])
low[a]=min(low[a],dfn[map[a][i]]);
}
if(low[a]==dfn[a])
{
int v;
count++;
belong[a]=count;
if(!test.empty())
v=test.top();
while(1)
{
belong[v]=count;
sign[v]=1;
test.pop();
if(a==v)
break;
if(!test.empty())
v=test.top();
}
}
}
int main()
{
int t,n,m,i,a,b,j;
cin>>t;
while(t--)
{
cin>>n>>m;
init();
for(i=1;i<=n;i++)
map[i].clear();
time=0;
count=0;
for(i=1;i<=m;i++)
{
cin>>a>>b;
map[a].push_back(b);
}
for(i=1;i<=n;i++)
{
if(!loop[i])
tarjan(i);
}
if(count==1)
{
cout<<0<<endl;
continue;
}
for(i=1;i<=n;i++)
{
for(j=0;j<map[i].size();j++)
{
if(belong[i]!=belong[map[i][j]])
{
out[belong[i]]++;
in[belong[map[i][j]]]++;
}
}
}
int sum1=0,sum2=0;
for(i=1;i<=count;i++)
{
if(!in[i])
sum1++;
if(!out[i])
sum2++;
}
if(sum1<sum2)
sum1=sum2;
cout<<sum1<<endl;
}
return 0;
}