【强连通分量缩点】poj 1236 Network of Schools
【题意】
- 给定一个有向图,求:
- (1)至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
- (2)至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点
【思路】
- (1)强连通分量缩点后形成一个有向无环图,只要选择入度为0的顶点,其他顶点都可以被到达
- (2)等价于一个有向无环图加最少加多少条边能够变成一个强连通图,取出度为0的点的个数和入度为0的点的个数的max,因为出度为0的点要加一条出边,入度为0的点要加一条入边
- (2)特判特殊情况:强连通分量只有一个,这时虽然入度为0和出度为0的点都是一个,但不需要加边
【AC】
1 //#include<bits/stdc++.h> 2 #include<iostream> 3 #include<cstdio> 4 #include<string> 5 #include<cstring> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 typedef long long ll; 10 int n; 11 const int maxm=10002; 12 const int maxn=1e2+2; 13 struct edge 14 { 15 int to; 16 int nxt; 17 }e[maxm]; 18 int head[maxn],tot; 19 int dfn[maxn],low[maxn],id; 20 int S[maxn],top; 21 int num,belong[maxn]; 22 bool vis[maxn]; 23 bool in[maxn]; 24 bool out[maxn]; 25 void init() 26 { 27 memset(head,-1,sizeof(head)); 28 tot=0; 29 id=0; 30 top=0; 31 num=0; 32 memset(dfn,0,sizeof(dfn)); 33 memset(low,0,sizeof(low)); 34 memset(S,0,sizeof(S)); 35 memset(vis,false,sizeof(vis)); 36 memset(belong,0,sizeof(belong)); 37 memset(in,false,sizeof(in)); 38 memset(out,false,sizeof(out)); 39 } 40 void addedge(int u,int v) 41 { 42 e[tot].to=v; 43 e[tot].nxt=head[u]; 44 head[u]=tot++; 45 } 46 void tarjan(int u) 47 { 48 dfn[u]=low[u]=++id; 49 S[++top]=u; 50 vis[u]=true; 51 for(int i=head[u];i!=-1;i=e[i].nxt) 52 { 53 int v=e[i].to; 54 if(!dfn[v]) 55 { 56 tarjan(v); 57 low[u]=min(low[u],low[v]); 58 } 59 else if(vis[v]) low[u]=min(low[u],dfn[v]); 60 } 61 if(dfn[u]==low[u]) 62 { 63 num++; 64 while(1) 65 { 66 belong[S[top]]=num; 67 vis[S[top]]=false; 68 if(S[top--]==u) break; 69 } 70 } 71 72 } 73 74 int main() 75 { 76 while(~scanf("%d",&n)) 77 { 78 init(); 79 for(int i=1;i<=n;i++) 80 { 81 int x; 82 while(1) 83 { 84 scanf("%d",&x); 85 if(x==0) break; 86 addedge(i,x); 87 } 88 } 89 for(int i=1;i<=n;i++) 90 { 91 if(!dfn[i]) tarjan(i); 92 } 93 for(int u=1;u<=n;u++) 94 { 95 for(int i=head[u];i!=-1;i=e[i].nxt) 96 { 97 int v=e[i].to; 98 if(belong[u]==belong[v]) continue; 99 in[belong[v]]=true; 100 out[belong[u]]=true; 101 } 102 } 103 int ans1=0; 104 int ans2=0; 105 for(int i=1;i<=num;i++) 106 { 107 if(!in[i]) ans1++; 108 if(!out[i]) ans2++; 109 } 110 int ans=max(ans1,ans2); 111 if(num==1) ans=0; 112 printf("%d\n%d\n",ans1,ans); 113 } 114 return 0; 115 }
【大佬博客】
www.cnblogs.com/kuangbin/archive/2011/08/07/2130277.html
解题思路:
1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少
在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少
加边的方法:
要为每个入度为0的点添加入边,为每个出度为0的点添加出边
假定有 n 个入度为0的点,m个出度为0的点,如何加边?
把所有入度为0的点编号 0,1,2,3,4 ....N -1
每次为一个编号为i的入度0点可达的出度0点,添加一条出边,连到编号为(i+1)%N 的那个出度0点,
这需要加n条边
若 m <= n,则
加了这n条边后,已经没有入度0点,则问题解决,一共加了n条边
若 m > n,则还有m-n个入度0点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加m-n条边。
所以,max(m,n)就是第二个问题的解
此外:当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加清单的项了,所以答案是1,0;