题意:给定有向图,问是的图连同的最小数量的边是多少
思路:把有向图转换成无向图,dfs染色,处理出若干个无向图联通块。我们再考察每个无向图联通块中的有向边,如果他们构成了一个有向无环图(DAG),那么说明图中的点是可以拓扑排序的。那么使用点数减一条有向边就能构造出这个拓扑关系(按拓扑序连成一条链)。如果不是一个DAG,那么无法拓扑排序。那我可以将所有点连成一个环,这样任意两点之间都是可达的,需要的边数即使联通块包含的点数。判断DAG的方法就是类似拓扑排序,记录每个点的出度,先找一个出度是0的点,不断拆边,最后看看是不是所有的边都能拆掉。所以对于每个联通块,判断它的有向边是否构成了DAG,是的话对答案的贡献就是点数减一,不是的话贡献就是点数。
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=100010;
int N,M;
vector<int> g[maxn],has[maxn],to[maxn];
int deg[maxn];
int idx;
int vis[maxn];
void dfs(int u)
{
vis[u]=idx;
int len=g[u].size();
for(int i=0;i<len;i++)
{
int v=g[u][i];
if(vis[v])continue;
dfs(v);
}
}
bool can(int id)
{
int len=has[id].size();
queue<int> q;
for(int i=0;i<len;i++)
if(deg[has[id][i]]==0)q.push(has[id][i]);
while(!q.empty())
{
int u=q.front();q.pop();
len=to[u].size();
for(int i=0;i<len;i++)
{
deg[to[u][i]]--;
if(!deg[to[u][i]])
q.push(to[u][i]);
}
}
len=has[id].size();
for(int i=0;i<len;i++)
if(deg[has[id][i]])return true;
return false;
}
int main()
{
while(scanf("%d%d",&N,&M)!=EOF)
{
int u,v;
memset(deg,0,sizeof(deg));
for(int i=0;i<=N;i++){to[i].clear();g[i].clear();has[i].clear();}
for(int i=1;i<=M;i++)
{
scanf("%d%D",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
to[v].push_back(u);
deg[u]++;
}
idx=1;
memset(vis,0,sizeof(vis));
for(int i=1;i<=N;i++)
{
if(!vis[i])
{
dfs(i);
idx++;
}
}
for(int i=1;i<=N;i++)
has[vis[i]].push_back(i);
int ans=0;
for(int i=1;i<idx;i++)
if(can(i))ans++;
printf("%d\n",N-idx+ 1+ans);
}
return 0;
}