zoukankan      html  css  js  c++  java
  • 连通性 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("
    ");
    90             if(flag==0)
    91                 printf("
    ");        
    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
    0
    ");
     95                 continue;
     96             }
     97             int as=solve();
     98             printf("%d
    ",a);
     99             printf("%d
    ",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
    ");
    114             else
    115                 printf("Yes
    ");
    116             
    117     }
    118     return 0;
    119 }
    View Code
  • 相关阅读:
    二维数组最大关联子数组
    四则运算(终极版)
    最大子数组
    四则运算(三) 记录日志
    四则运算(三)
    四则运算记录日志
    四则运算(二)
    简单web四则运算出题
    Daily Scrum
    Daily Scrum
  • 原文地址:https://www.cnblogs.com/assult/p/3312364.html
Copyright © 2011-2022 走看看