题目链接:http://vjudge.net/contest/141990#overview
题意是告诉你有n个命题,m条递推关系,表示某个命题可以推出另外一个命题。
现在问你至少在增加多少个递推关系可以保证所有命题两两互推。
把命题看成一个结点,推导看成有向边,就是n个结点,m 条有向边,要求添加尽量少的边,使得新图强连通。
首先找出强连通分量,把每个强连通分量缩成一个点,得到DAG。设有 a 个结点入度为 0 ,b 个结点出度为 0 ,max(a,b),就是答案。如下图:
入度为 0 的集合 为 1,出度为 0 的集合 为 2,要加两条红边才能 互相到达(强连通)。
先标记所有强连通图的入度出度 1 ,要是有点相同,标记为 0 ,统计 入度为 1 ,出度为 1 的个数。
如果原图只有一个强连通分量 ans = 0 不需要加边。
#include <bits/stdc++.h> using namespace std; const int Maxn = 20000 + 10; vector<int> G[Maxn]; int pre[Maxn]; int lowlink[Maxn]; int sccno[Maxn]; int dfs_clock; int scc_cnt; stack<int> S; void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for(int i=0; i<G[u].size(); i++) { int v = G[u][i]; if(!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u],lowlink[v]); } else if(!sccno[v]) { lowlink[u] = min(lowlink[u],pre[v]); } } if(lowlink[u]==pre[u]) { scc_cnt ++; for(;;) { int x = S.top(); S.pop(); sccno[x] = scc_cnt; if(x==u) break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; memset(sccno,0,sizeof(sccno)); memset(pre,0,sizeof(pre)); for(int i=0; i<n; i++) { if(!pre[i]) dfs(i); } } int main() { int n,m; int t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=0; i<n; i++) G[i].clear(); for(int i=0; i<m; i++) { int u,v; scanf("%d%d",&u,&v); u--; v--; G[u].push_back(v); } find_scc(n); int in0[Maxn]; int out0[Maxn]; for(int i=1; i<=scc_cnt; i++) { in0[i] = 1; out0[i] = 1; } for(int u=0; u<n; u++) { for(int i=0; i<G[u].size(); i++) { int v = G[u][i]; if(sccno[u]!=sccno[v]) { in0[sccno[v]] = 0; out0[sccno[u]] = 0; } } } int maxin = 0,maxout = 0; for(int i=1; i<=scc_cnt; i++) { if(in0[i]) maxin ++; if(out0[i]) maxout ++; } int ans = max(maxin,maxout); if(scc_cnt==1) puts("0"); else printf("%d ",ans); } return 0; }