题意:给n个点,然后给出m个条件a、b,表示a可到达b,求最少需要建多少条有向边才能满足所有条件
思路:按所给的条件建有向图,可以发现,每一个联通块都是一个弱联通图,若一个m个点的联通块,如果没有环的话最少需要m-1条有向边,即连成一条链,若存在环,只需要m条边就可以连成一个强联通图,自然会满足联通块的所有条件,那么用拓扑判断有向环即可,注意建图的时候还是要建无向图,但是入度只记录条件给的边,因为如果建无向图,那么在dfs的时候不一定可以一次找到弱联通块所有的点,比如 1 2, 2 1这样的数据,所以建双向边,在每次dfs的时候把入度为0的点加入拓扑队列即可
AC代码:
#include "iostream" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector" #include "set" #include "map" #include "algorithm" #include "stdio.h" #include "math.h" #pragma comment(linker, "/STACK:102400000,102400000") #define ll long long #define endl (" ") #define bug(x) cout<<x<<" "<<"UUUUU"<<endl; #define mem(a,x) memset(a,x,sizeof(a)) #define mp(x,y) make_pair(x,y) #define pb(x) push_back(x) #define ft first #define sd second #define lrt (rt<<1) #define rrt (rt<<1|1) using namespace std; const long long INF = 1e18+1LL; const int inf = 1e9+1e8; const int N=1e5+100; const ll mod=1e9+7; int in[N],head[N],to[N<<1],nex[N<<1],tot=1; int n,m,ans,vis[N],mk[N]; queue<int> Q; void add(int u, int v){ to[tot]=v; nex[tot]=head[u]; head[u]=tot++; } int dfs(int u){ int ret=1; vis[u]=1; if(in[u]==0){ Q.push(u); mk[u]=1; } for(int i=head[u]; i!=-1; i=nex[i]){ int v=to[i]; if(!vis[v]) ret+=dfs(v); } return ret; } bool topo(int u, int c){ int k=0; while(!Q.empty()){ int now=Q.front(); Q.pop(), k++; for(int i=head[now]; i!=-1; i=nex[i]){ int v=to[i]; in[v]--; if(!mk[v] && in[v]==0){ Q.push(v); mk[v]=1; } } } return k==c; } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); mem(head,-1); cin>>n>>m; for(int _=1; _<=m; ++_){ int u, v; cin>>u>>v; add(u,v); add(v,u); in[v]++; } for(int i=1; i<=n; ++i){ if(!vis[i] && in[i]==0){ while(!Q.empty()) Q.pop(); int c=dfs(i); if(topo(i,c)) ans+=c-1; else ans+=c; } } for(int i=1; i<=n; ++i){ if(!vis[i]) ans++; } cout<<ans<<endl; return 0; }