连通性 1.有向图的强连通图 scc
Trajan算法求出图中所有的scc :
考虑强连通分量c 设其中第一个被发现的点是x 则c中其他店都是x的后代 我们希望在x访问完成后立即输出c 这样就可以在一节课dfs数中区分出所有的 scc了。因此问题的 关键是判断一个点是不是一个scc中最早发现的点。联系求割点算法, 若是一个点u,low[u]==pre[u] 即一个点不能连到比他更早的祖先节点 而最多只能连到他自己 就说明他是这个scc中最早发现的点
算法模板:
#include<iostream> #include<stdio.h> #include<string.h> #include<stack> using namespace std; int pre[5002],low[5002],lt_num,c,scc_num[5002],scc,out0[5002],n,adj[5002],num,flag; stack <int >s; void dfs(int u) { int i,v; pre[u]=low[u]=c++; s.push (u); for(i=adj[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(!pre[v])//!scc_num[v] { dfs(v); if(low[v]<low[u]) low[u]=low[v]; } else if(low[v]<low[u]&&!scc_num[v]) low[u]=low[v]; } if(low[u]==pre[u]) //是该连通分量的 第一个点 { scc++; while(1) { int t=s.top ();s.pop (); scc_num[t]=scc; //scc_num[t]是第scc个强连通分量; if(t==u) break; } } }
poj 1553 The Bottom of a Graph http://poj.org/problem?id=2553
【题意】:给出一张图 求出度为0 的scc 输出为这些scc 里面的所有点,从大到小。
【注意】顶点数有5000 所以不能开邻接矩阵 要存边
【思路】:先求出每个点所在的scc的编号 再扫描次所有边...用 out0[i] 记录个点所在的scc的出度是否为0...最后扫一遍所有点..其所在的强连通分量的出度为0则输出这个点...这样即又保证了从大到小的输出...
贴一下 存边的代码:
1 struct E{int to;int next;} edge[20000000]; 2 int adj[5002];
3 void add(int a,int b) 4 { 5 edge[num].to=b; 6 edge[num].next=adj[a]; 7 adj[a]=num++; 8 } 9 10 11 //主函数中 12 memset(adj,-1,sizeof(adj)); 13 num=0; 14 while(m--) 15 { 16 scanf("%d%d",&a,&b); 17 add(a,b); 18 }
完整的代码:
1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 #include<stack> 5 using namespace std; 6 7 struct E{int to;int next;} edge[20000000]; 8 int pre[5002],low[5002],lt_num,c,scc_num[5002],scc,out0[5002],n,adj[5002],num,flag; 9 stack <int >s; 10 11 void add(int a,int b) 12 { 13 edge[num].to=b; 14 edge[num].next=adj[a]; 15 adj[a]=num++; 16 } 17 18 void dfs(int u) 19 { 20 int i,v; 21 pre[u]=low[u]=c++; 22 s.push (u); 23 for(i=adj[u];i!=-1;i=edge[i].next) 24 { 25 v=edge[i].to; 26 if(!pre[v])//!scc_num[v] 27 { 28 dfs(v); 29 if(low[v]<low[u]) 30 low[u]=low[v]; 31 } 32 else if(low[v]<low[u]&&!scc_num[v]) 33 low[u]=low[v]; 34 } 35 if(low[u]==pre[u]) //是该连通分量的 第一个点 36 { 37 scc++; 38 while(1) 39 { 40 int t=s.top ();s.pop (); 41 scc_num[t]=scc; //scc_num[t]是第scc个强连通分量; 42 if(t==u) 43 break; 44 } 45 } 46 } 47 48 void solve() 49 { 50 int i,j,v; 51 memset(out0,0,sizeof(out0)); 52 for(i=1;i<=n;i++) 53 for(j=adj[i];j!=-1;j=edge[j].next) 54 { 55 v=edge[j].to; 56 if(scc_num[i]!=scc_num[v]) 57 out0[scc_num[i]]=1; 58 } 59 for(i=1;i<=n;i++) 60 if(!out0[scc_num[i]]) 61 { 62 flag=1; 63 printf("%d ",i); 64 } 65 } 66 67 int main() 68 { 69 int i,m,a,b; 70 while(scanf("%d",&n)) 71 { 72 flag=0; 73 if(n==0) 74 break; 75 scanf("%d",&m); 76 memset(adj,-1,sizeof(adj)); 77 while(m--) 78 { 79 scanf("%d%d",&a,&b); 80 add(a,b); 81 } 82 scc=0;c=1;num=0; 83 memset(pre,0,sizeof(pre)); 84 memset(scc_num,0,sizeof(scc_num)); 85 for(i=1;i<=n;i++) 86 if(!pre[i]) 87 dfs(i); 88 solve(); 89 printf("\n"); 90 if(flag==0) 91 printf("\n"); 92 } 93 return 0; 94 }
poj 1236 Network of Schools http://poj.org/problem?id=1236
【题意+思路】:给出一张有向图 N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。即输出为2行 第一行是这张图求出缩点后入度为0的点数 第二行为至少添加几条边可以使得整张图变成一个强连通分支(缩点后求入度为0和出度为0的点数 取最大 本来就只有一个强连通分支就直接输0了)。
1 #include<iostream> 2 #include<stdio.h> 3 #include<vector> 4 #include<string.h> 5 #include<stack> 6 using namespace std; 7 vector <int > g[1002]; 8 int pre[1002],low[1002],lt_num,c,scc_num[1002],scc,in0[1002],out0[1002],n,a,b; 9 10 stack <int >s; 11 void dfs(int u) 12 { 13 int i,v; 14 pre[u]=low[u]=c++; 15 s.push (u); 16 for(i=0;i<g[u].size ();i++) 17 { 18 v=g[u][i]; 19 if(!pre[v])//!scc_num[v] 20 { 21 dfs(v); 22 if(low[v]<low[u]) 23 low[u]=low[v]; 24 } 25 else if(low[v]<low[u]&&!scc_num[v]) 26 low[u]=low[v]; 27 } 28 if(low[u]==pre[u]) //是该连通分量的 第一个点 29 { 30 scc++; 31 while(1) 32 { 33 int t=s.top ();s.pop (); 34 scc_num[t]=scc; //scc_num[t]是第scc个强连通分量; 35 if(t==u) 36 break; 37 } 38 39 } 40 41 } 42 43 int solve() 44 { 45 if(scc==1) 46 return 0; 47 int i,j,v; 48 a=0,b=0; 49 for(i=0;i<=scc;i++) 50 in0[i]=out0[i]=0; 51 for(i=1;i<=n;i++) 52 for(j=0;j<g[i].size ();j++) 53 { 54 v=g[i][j]; 55 if(scc_num[i]!=scc_num[v]) 56 out0[scc_num[i]]++,in0[scc_num[v]]++; 57 } 58 for(i=1;i<=scc;i++) 59 { 60 if(!in0[i]) 61 a++; 62 if(!out0[i]) 63 b++; 64 } 65 return a>b?a:b; 66 } 67 68 int main() 69 { 70 int i,q; 71 while(scanf("%d",&n)!=EOF) 72 { 73 for(i=1;i<=n;i++) 74 g[i].clear (); 75 for(i=1;i<=n;i++) 76 { 77 while(scanf("%d",&q)) 78 { 79 if(q==0) 80 break; 81 g[i].push_back (q); 82 } 83 } 84 c=1; scc=0;lt_num=0; 85 memset(pre,0,sizeof(pre)); 86 memset(scc_num,0,sizeof(scc_num)); 87 for(i=1;i<=n;i++) 88 if(!pre[i]) 89 { 90 lt_num++; 91 dfs(i); 92 } 93 if(scc==1){ 94 printf("1\n0\n"); 95 continue; 96 } 97 int as=solve(); 98 printf("%d\n",a); 99 printf("%d\n",as); 100 while(!s.empty ()) 101 s.pop (); 102 } 103 return 0; 104 }
poj 2762 Going from u to v or from v to u? http://poj.org/problem?id=2762
【题意+思路】:给你一个有向图,问你这个图是否符合以下的条件:对于图上任意两点x , y,都存在一条有向路径,从x到y或从y到x。(注意是或 这里要求的是弱连通)。强连通分支里的点都可以两两互达 两个连通分支有直接或间接的连边 则位于两个强连通分支里的两个点可以从一个点到达另一个点(不能互达) 而两点都不能互达 用拓扑排序来判断,如果某次删除点的时候发现两个入度为0的点,则说明这两个点只能由已经被删掉的点到达,也就是说这两个点互相不可达。
1 #include<iostream> 2 #include<vector> 3 #include<string.h> 4 #include<stack> 5 using namespace std; 6 int pre[1002],low[1002],c,sccnum[1002],degree[1002],scc,n; 7 vector <int > g[1002],gg[1002]; 8 stack <int > s; 9 10 int min(int a,int b) 11 { 12 return a<b?a:b; 13 } 14 15 int dfs(int u) 16 { 17 s.push (u); 18 low[u]=pre[u]=c++; 19 for(int i=0;i<g[u].size ();i++) 20 { 21 int v=g[u][i]; 22 if(!pre[v]) 23 { 24 int lowv=dfs(v); 25 low[u]=min(low[u],lowv); 26 } 27 else if(sccnum[v]==0) 28 low[u]=min(low[u],pre[v]); 29 } 30 if(pre[u]==low[u]) 31 { 32 scc++; 33 while(1) 34 { 35 int t=s.top (); s.pop(); 36 sccnum[t]=scc; 37 if(u==t) 38 break; 39 } 40 } 41 42 return low[u]; 43 } 44 45 int solve() 46 { 47 int i,j,cur; 48 for(i=1;i<=scc;i++) 49 gg[i].clear (); 50 memset(degree,0,sizeof(degree)); 51 for(i=1;i<=n;i++) 52 for(j=0;j<g[i].size ();j++) 53 { 54 int v=g[i][j]; 55 if(sccnum[i]!=sccnum[v]) 56 { 57 degree[sccnum[v]]++; //入度 58 gg[sccnum[i]].push_back (sccnum[v]); 59 } 60 } 61 int cnt=0; 62 for(i=1;i<=scc;i++) 63 if(degree[i]==0) 64 { 65 cnt++; 66 cur=i; 67 if(cnt>1) 68 return 0; 69 } 70 int num=scc,temp=-1; 71 while(num--) 72 { 73 cnt=0; 74 for(i=0;i<gg[cur].size ();i++) 75 { 76 int v=gg[cur][i]; 77 degree[v]--; 78 if(degree[v]==0) 79 { 80 cnt++; 81 if(cnt>1) 82 return 0; 83 temp=v; 84 } 85 } 86 cur=temp; 87 } 88 return 1; 89 } 90 91 int main() 92 { 93 int t,m,a,b,i,p=1; 94 scanf("%d",&t); 95 while(t--) 96 { 97 scanf("%d%d",&n,&m); 98 for(i=1;i<=n;i++) 99 g[i].clear(); 100 while(m--) 101 { 102 scanf("%d%d",&a,&b); 103 g[a].push_back (b); 104 } 105 memset(pre,0,sizeof(pre)); 106 memset(sccnum,0,sizeof(sccnum)); 107 int cnt=0; c=1; scc=0; 108 for(i=1;i<=n;i++) 109 if(!pre[i]) 110 dfs(i); 111 int ww=solve(); 112 if(ww==0) 113 printf("No\n"); 114 else 115 printf("Yes\n"); 116 117 } 118 return 0; 119 }