题目链接:https://vjudge.net/problem/UVALive-4287
题意:有n个命题,已知其中的m个推导,要证明n个命题全部等价(等价具有传递性),最少还需要做出几次
推导
解法:由已知的推导可以建一张无向图,则问题变成了最少需要增加几条边能使图变成强连通图。找出所有的
强连通分量,将每一个连通分量视作一个大节点,则整张图变成了一张DAG。设出度为0的大节点个数为a,
入度为0的大节点个数为b,则答案就是max(a,b)。为什么是这样呢?因为要使等价证明前进下去,每个大节
点的出度和入度都必须不能是0,注意特判整个图是强连通分量的情况
///UVALIVE 4287
#include <bits/stdc++.h>
using namespace std;
const int maxn = 20005;
int n, m, low[maxn], dfn[maxn], dfs_clk, scc, top;
int in[maxn], out[maxn], s[maxn], belong[maxn], head[maxn], edgecnt;
bool inq[maxn];
struct edge{
int v, nxt;
edge(){}
}E[maxn*4];
void init(){
memset(head, -1, sizeof(head));
edgecnt=0;
}
void add(int u, int v){
E[edgecnt].v = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++;
}
void tarjan(int x){
int now=0;
dfn[x]=low[x]=++dfs_clk;
s[++top]=x; inq[x]=1;
for(int i=head[x]; i+1; i=E[i].nxt){
int to=E[i].v;
if(!dfn[to]){
tarjan(to);
low[x]=min(low[x],low[to]);
}
else if(inq[to]){
low[x]=min(low[x],dfn[to]);
}
}
if(low[x]==dfn[x]){
++scc;
while(now!=x){
now=s[top];top--;
belong[now]=scc;
inq[now]=0;
}
}
}
int main(){
int T; scanf("%d", &T);
while(T--){
scanf("%d%d", &n,&m);
init();
while(m--){
int u, v;
scanf("%d%d", &u,&v);
add(u, v);
}
memset(inq, 0, sizeof(inq));
memset(low, 0, sizeof(low));
memset(dfn, 0, sizeof(dfn));
memset(belong, 0, sizeof(belong));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(s, 0, sizeof(s));
dfs_clk = scc = top = 0;
for(int i=1; i<=n; i++){
if(!dfn[i]){
tarjan(i);
}
}
for(int i=1; i<=scc; i++){
in[i] = out[i] = 0;
}
int a = 0, b = 0;
for(int i=1; i<=n; i++){
for(int j=head[i]; j+1; j=E[j].nxt){
int v = E[j].v;
if(belong[i]!=belong[v]){
out[belong[i]]=in[belong[v]]=1;
}
}
}
for(int i=1; i<=scc; i++){
if(!in[i]) a++;
if(!out[i]) b++;
}
int ans = max(a, b);
if(scc==1){
ans=0;
}
printf("%d\n", ans);
}
return 0;
}