连通性 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 }
View Code

 

 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 }
View Code

 

 

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 }
View Code

 

 

 

 

 

 

posted @ 2013-09-10 15:56  galaxy77  阅读(523)  评论(0编辑  收藏  举报