UVALive-4287 Proving Equivalences (有向图的强连通分量)
题目大意:有n个命题,已知其中的m个推导,要证明n个命题全部等价(等价具有传递性),最少还需要做出几次推导。
题目分析:由已知的推导可以建一张无向图,则问题变成了最少需要增加几条边能使图变成强连通图。找出所有的强连通分量,将每一个连通分量视作一个大节点,则整张图变成了一张DAG。设出度为0的大节点个数为a,入度为0的大节点个数为b,则答案就是max(a,b)。为什么是这样呢?因为要使等价证明前进下去,每个大节点的出度和入度都必须不能是0。
代码如下:
# include<iostream> # include<cstdio> # include<vector> # include<stack> # include<cstring> # include<algorithm> using namespace std; const int maxn=20005; int n,m,low[maxn],pre[maxn],sccno[maxn],in[maxn],out[maxn],dfs_cnt,scc_cnt; stack<int>S; vector<int>G[maxn]; void dfs(int u) { low[u]=pre[u]=++dfs_cnt; S.push(u); for(int i=0;i<G[u].size();++i){ int v=G[u][i]; if(!pre[v]){ dfs(v); low[u]=min(low[u],low[v]); }else if(!sccno[v]) low[u]=min(pre[v],low[u]); } if(low[u]==pre[u]){ ++scc_cnt; while(1){ int x=S.top(); S.pop(); sccno[x]=scc_cnt; if(x==u) break; } } } void findScc() { memset(low,0,sizeof(low)); memset(pre,0,sizeof(pre)); memset(sccno,0,sizeof(sccno)); dfs_cnt=scc_cnt=0; for(int i=0;i<n;++i) if(!pre[i]) dfs(i); } void read() { int a,b; scanf("%d%d",&n,&m); for(int i=0;i<n;++i) G[i].clear(); while(m--) { scanf("%d%d",&a,&b); --a,--b; G[a].push_back(b); } } void solve() { for(int i=1;i<=scc_cnt;++i) in[i]=out[i]=1;///先假设所有分量的出度和入度都是0; for(int i=0;i<n;++i) for(int j=0;j<G[i].size();++j) if(sccno[i]!=sccno[G[i][j]]) out[sccno[i]]=in[sccno[G[i][j]]]=0;///如果i和G[i][j]不在一个分量内,则其对应的出度和入度不是0; int a=0,b=0; for(int i=1;i<=scc_cnt;++i){ if(in[i]) ++a; if(out[i]) ++b; } int ans=max(a,b); if(scc_cnt==1) ans=0; printf("%d\n",ans); } int main() { int T; scanf("%d",&T); while(T--) { read(); findScc(); solve(); } return 0; }