建立正向图g 、反向图rg 两遍dfs
判断强联通分量变成1最少加的边数
需要每个强联通分量缺少的出度和入度的最大值。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
#define maxn 20010
int n; //顶点数
vector<int>rg[maxn];
vector<int>g[maxn];
vector<int>vs; //后序遍历顺序的顶点列表
bool vis[maxn];
int scin[maxn],scout[maxn];
int cmp[maxn]; //所属强联通分量的拓扑序
void add(int from,int to)
{
g[from].push_back(to);
rg[to].push_back(from);
}
void dfs(int v)
{
vis[v]=1;
for(int i=0;i<g[v].size();++i)
{
if(!vis[g[v][i]])dfs(g[v][i]);
}
vs.push_back(v);
}
void rdfs(int v,int k)
{
vis[v]=1;
cmp[v]=k;
for(int i=0;i<rg[v].size();++i)
{
if(!vis[rg[v][i]]){rdfs(rg[v][i],k);}
}
}
int scc()
{
memset(vis,0,sizeof vis);
memset(cmp,0,sizeof cmp);
vs.clear();
for(int i=1;i<=n;i++)
{
if(!vis[i])dfs(i);
}
memset(vis,0,sizeof vis);
int k=0;
for(int i=vs.size()-1;i>=0;i--)
{
if(!vis[vs[i]])rdfs(vs[i],k++);
}
return k;
}
int main()
{
int m,a,b;
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0)break;
memset(scin,0,sizeof scin);
memset(scout,0,sizeof scout);
for(int i=0;i<m;++i)
{
scanf("%d%d",&a,&b);
add(a,b);
}
int sc=scc();
// printf("cc");
if(sc==1||n==1)
{
printf("0\n");
for(int i=0;i<=n;++i){g[i].clear();rg[i].clear();}
continue;
}
else
{
for(int i=1;i<=n;++i)
{
for(int j=0;j<g[i].size();++j)
{
if(cmp[i]!=cmp[g[i][j]])
{
scin[cmp[g[i][j]]]++;
scout[cmp[i]]++;
}
}
}
int num1=0,num2=0;
for(int i=0;i<sc;++i)
{
if(scin[i]==0)num1++;
if(scout[i]==0)num2++;
}
printf("%d\n",max(num1,num2));
}
for(int i=0;i<=n;++i){g[i].clear();rg[i].clear();}
}
}