HDU 2767 Proving Equivalences【强连通分量+缩点】
题意: 在有 n 个节点的有向图中 有 m 条边,问最少需要增加多少条边可以将原图变成一个强连通图。
分析: 因为图中每个环都满足强连通的性质,可以用强连通分量的算法将原图中的环进行染色缩点。
缩点的最大好处在于把一个杂乱无章的有向图变成一个有向无环图,而在有向无环图中,有两种点
比较特殊: 一种是入度为 0 的点,另一种是 出度为 0 的点。
题目问要增加多少条边使得原图变成强连通图,其实只要知道在树根个叶子之间加多少条边,假如 r
为根的个数, g 为叶子的个数,答案即为max(r,g)。
特殊情况是当缩点之后只有一个点时,答案为 0。
code:
View Code
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) const int maxn=20002; struct node { int to; int next; }q[60000]; int head[maxn]; int tot; void add(int s,int u) { q[tot].to=u; q[tot].next=head[s]; head[s]=tot++; } bool ins[maxn]; int color[maxn]; int dfn[maxn],low[maxn],stack[maxn]; int ti,sn,top; void tarjan(int u) { dfn[u]=low[u]=++ti; stack[++top]=u; ins[u]=true; int i,k; for(i=head[u];i;i=q[i].next) { k=q[i].to; if(dfn[k]==0) { tarjan(k); if(low[k]<low[u]) low[u]=low[k]; } else if(ins[k]&&dfn[k]<low[u]) low[u]=dfn[k]; } if(dfn[u]==low[u]) { sn++; do { k=stack[top--]; ins[k]=false; color[k]=sn; // 强连通分量染色 } while(k!=u); } } int id[maxn],od[maxn]; int main() { int t,i,j,k,a,b,n,m; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); tot=1; top=-1; sn=ti=0; clr(low); clr(dfn); clr(ins); clr(head); while(m--) { scanf("%d%d",&a,&b); add(a,b); } for(i=1;i<=n;i++) if(dfn[i]==0) tarjan(i); if(sn==1) printf("0\n"); else { clr(id); clr(od); int in=0,out=0; for(i=1;i<=n;i++) for(j=head[i];j;j=q[j].next) { k=q[j].to; if(color[i]!=color[k]) { id[color[k]]++; od[color[i]]++; } } for(i=1;i<=sn;i++) { if(id[i]==0) in++; if(od[i]==0) out++; } printf("%d\n",in>out?in:out); } } return 0; }