zoukankan      html  css  js  c++  java
  • tarjan总结

    先说一下割点跟割边吧。

    割桥就是如果一个连通图里删除这条边之后,这个图会变成两个连通图,那么这条边就称之为割桥。

    这是割桥的代码,里面呆着lca求法。

    割点和割桥的就是用一个时间戳和回到祖先确定。

    用dfs的时间戳可以看出。

    割点为low[v] >= dfn[u]

    割边为low[v] > dfn[u]。

    但是要注意的是割点的条件对于搜索树的根节点是要特殊处理的,当根节点的孩子大于1的时候就一定是割点。

     1 void tarjan(int u,int pre)
     2 {
     3     int v,i,j;
     4     dfn[u] = low[u] = ++dfsclock;
     5     loop(0,i,g[u].size())
     6     {
     7         v = g[u][i];
     8         if(!dfn[v])//保证是树枝边
     9         {
    10             tarjan(v,u);
    11             father[v] = u;//v的父亲是u
    12             low[u] = min(low[v],low[u]);
    13             if(low[v] > dfn[u])//加入v回到的祖先小于u那么u,v一定是桥。
    14             cut++;
    15             else
    16             merge(u,v);
    17         }
    18         else if(v != pre)//保证不是直向自己父亲的遍
    19         low[u] = min(low[u],dfn[v]);
    20     }
    21 }
    22 void lca(int u,int v)//最后的u和v是同一个最近祖先
    23 {
    24 
    25     while(u != v)
    26     {
    27         while(dfn[u] >= dfn[v] && u != v)//v遍历的比较早
    28         {
    29             if(merge(u,father[u]))//如果u的父亲和u不是在一个环里也就是不在缩点之后的同一个点,那么就是缩点之后的桥
    30             cut--;
    31             u = father[u];
    32         }
    33         while(dfn[v] >= dfn[u] && u != v)
    34         {
    35             if(merge(v,father[v]))
    36             cut--;
    37             v =  father[v];
    38         }
    39     }
    40 }
    View Code

    割点就是如果一个连通图删除了某个点和相关的边之后那么,这个图就是变成两个连通图, 这个点叫做割点。

    这是割点代码

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <iostream>
     4 #include <algorithm>
     5 #include <stdlib.h>
     6 #include <vector>
     7 #include <queue>
     8 #include <stack>
     9 #define loop(s,i,n) for(i = s;i < n;i++)
    10 #define cl(a,b) memset(a,b,sizeof(a))
    11 using namespace std;
    12 const int maxn = 205;
    13 vector<int>g[maxn];
    14 int is_cut[maxn],dfn[maxn],low[maxn];
    15 
    16 int dfsclock;
    17 int cut;
    18 void init(int n)
    19 {
    20     int i;
    21     for(i = 0;i <= n;i++)
    22     g[i].clear();
    23     dfsclock = 0;
    24     cl(is_cut,0);
    25     cl(low,0);
    26     cl(dfn,0);
    27     cut = 0;
    28     return ;
    29 }
    30 void tarjan(int u,int pre)
    31 {
    32     dfn[u] = low[u] = ++dfsclock;
    33     int i;
    34     int child = 0;
    35     for(i = 0;i < g[u].size();i++)
    36     {
    37         int v;
    38         v = g[u][i];
    39 
    40         if(!dfn[v])//保证是没访问过的树枝边。
    41         {
    42             child++;//这里要加一个孩子数,为的是后面对树根的判断。
    43             tarjan(v,u);
    44             if(low[v] < low[u])
    45             low[u] = low[v];
    46             if(low[v] >= dfn[u])//注意区别。割点与割桥代码差的很少,就这么一点。割点的自己点是可以回到自身的,但是割边不行假如回到了,那么说明是环就一定不是桥~
    47             is_cut[u] = 1;
    48         }
    49         else if(v != pre)
    50         low[u] = min(dfn[v],low[u]);
    51     }
    52     if(pre < 0 && child == 1)//树根的判断。
    53     is_cut[u] = 0;
    54 
    55     return ;
    56 }
    57 
    58 int main()
    59 {
    60     int u,v,n;
    61     while(scanf("%d",&n)&&n)
    62     {
    63         init(n);//我觉得我这个人活在世上就是为了考验我容忍傻逼的限度。老忘初始化~
    64 
    65         read();//就是见图过程,不谢了。
    66         tarjan(1,-1);//如果不是一个连通图,那么就for一下对每一个!dfn[i]做一次。
    67         int i;
    68         for(i = 1;i <= n;i++)
    69         {
    70             if(is_cut[i])
    71             cut++;
    72         }
    73         cout<<cut<<endl;
    74     }
    75     return 0;
    76 }
    View Code

     以上是无向图的代码,有向图的自己改一下吧~

    这是对于有向图的强连通分量,不多讲。想详细了解一下的话可以看这个:https://www.byvoid.com/blog/scc-tarjan

    贴下自己的代码,注意对重边情况要另外写哦~

    这是由重边情况的。

     1 const int maxn = 200005;
     2 bool vis[2000005];
     3 struct edge
     4 {
     5     int v,next;
     6     edge()
     7     {
     8         next = -1;
     9     }
    10 }edges[maxn*10-45];
    11 struct ed
    12 {
    13     int u,v;
    14 }e[maxn*10-45];
    15 int dfn[200005],low[200005],belong[200005];
    16 bool inst[200005];
    17 int g[maxn];
    18 vector<int>ng[maxn];
    19 
    20 stack<int>st;
    21 int bcnt,cnt,time;
    22 
    23 void init(int n)
    24 {
    25     int i;
    26     for(i =0;i <= n;i++)
    27     g[i] = -1;
    28     time = 0;bcnt = cnt = 0;
    29     return ;
    30 }
    31 void addedge(int u,int v,int val)
    32 {
    33     struct edge o;
    34     edges[cnt].v = v;
    35     edges[cnt].next = g[u];
    36     g[u] = cnt;
    37     cnt++;
    38 
    39     return ;
    40 }
    41 void tarjan(int i)
    42 {
    43     int j;
    44     dfn[i] = low[i] = ++time;
    45     inst[i] = 1;
    46     st.push(i);
    47 
    48     for(j = g[i];j != -1;j = edges[j].next)
    49     {
    50         if(vis[j]) continue;
    51         vis[j] = vis[j^1] = 1;
    52         int v;
    53         v = edges[j].v;
    54         if(!dfn[v])
    55         {
    56             tarjan(v);
    57             low[i] = min(low[i],low[v]);
    58         }
    59         else if(inst[v])
    60         low[i] = min(low[i],dfn[v]);
    61     }
    62     int k;
    63     if(dfn[i] == low[i])
    64     {
    65 
    66         bcnt++;
    67         do
    68         {
    69             k = st.top();
    70             st.pop();
    71             inst[k] = 0;
    72             belong[k] = bcnt;
    73 
    74         }
    75         while(k != i);
    76     }
    77 
    78 }
    79 void tarjans(int n)
    80 {
    81     int i;
    82     bcnt = time = 0;
    83     while(!st.empty())st.pop();
    84     memset(dfn,0,sizeof(dfn));
    85 
    86     memset(inst,0,sizeof(inst));
    87     memset(belong,0,sizeof(belong));
    88     for(i = 1;i <= n;i++)
    89     if(!dfn[i])tarjan(i);
    90 }
    View Code

    不考虑重边、

     1 struct edge
     2 {
     3     int u,v,val;
     4 };
     5 int dfn[10050],low[10050],belong[10050],inst[10050];
     6 int pnum[10050];
     7 int in[10050];
     8 int inside[10050];
     9 int out[10050];
    10 vector<edge> edges,es;
    11 vector<int>g[maxn];
    12 vector<int>ng[maxn];
    13 stack<int>st;
    14 int bcnt,cnt,dfsclock;
    15 int max(int a,int b)
    16 {
    17     if(a > b)
    18     return a;
    19 
    20     return b;
    21 }
    22 void init(int n)
    23 {
    24     int i;
    25     for(i =0;i <= n;i++)
    26     g[i].clear();
    27 
    28     edges.clear();
    29 
    30     es.clear();
    31     dfsclock = 0;bcnt = cnt = 0;
    32     return ;
    33 }
    34 void addedge(int u,int v,int val)
    35 {
    36     edges.push_back((edge){u,v,1});
    37     g[u].push_back(cnt);
    38     cnt++;
    39 
    40     return ;
    41 }
    42 void tarjan(int i)
    43 {
    44     int j;
    45     dfn[i] = low[i] = ++dfsclock;
    46     inst[i] = 1;
    47     st.push(i);
    48 
    49     for(j = 0;j < g[i].size();j++)
    50     {
    51         edge e;
    52         e = edges[g[i][j]];
    53         int v;
    54         v = e.v;
    55         if(!dfn[v])
    56         {
    57             tarjan(v);
    58             low[i] = min(low[i],low[v]);
    59         }
    60         else if(inst[v] && dfn[v] < low[i])
    61         low[i] = dfn[v];
    62     }
    63     if(dfn[i] == low[i])
    64     {
    65         bcnt++;
    66         do
    67         {
    68             j = st.top();
    69             st.pop();
    70             inst[j] = 0;
    71             belong[j] = bcnt;
    72 
    73         }
    74         while(j != i);
    75     }
    76 
    77 }
    78 void tarjans(int n)
    79 {
    80     int i;
    81     bcnt = dfsclock = 0;
    82     while(!st.empty())st.pop();
    83     memset(dfn,0,sizeof(dfn));
    84 
    85     memset(inst,0,sizeof(inst));
    86     memset(belong,0,sizeof(belong));
    87     for(i = 1;i <= n;i++)
    88     if(!dfn[i])tarjan(i);
    89 }
    View Code

     对于双连通分支,包括点连通和边连通。

    对于点双连通分支,实际上在求割点的过程中就能顺便把每个点双连通分支求出。建立一个栈,存储当前双连通分支,在搜索图时,每找到一条树枝边或后向边(非横叉边),就把这条边加入栈中。如果遇到某时满足DFS(u)<=Low(v),说明u是一个割点,同时把边从栈顶一个个取出,直到遇到了边(u,v),取出的这些边与其关联的点,组成一个点双连通分支。割点可以属于多个点双连通分支,其余点和每条边只属于且属于一个点双连通分支。

        对于边双连通分支,求法更为简单。只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分支。桥不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。

    我的代码(太困了,先贴上睡觉去)

    边连通

     1 void tarjan(int u,int pre)
     2 {
     3     int v,i,j;
     4     dfn[u] = low[u] = ++dfsclock;
     5     s.push(u);
     6     loop(0,i,g[u].size())
     7     {
     8         v = g[u][i];
     9 
    10         if(v != pre)
    11         {
    12             if(!dfn[v])//保证是树枝边
    13             {
    14                 tarjan(v,u);
    15 
    16                 low[u] = min(low[v],low[u]);
    17 
    18             }
    19             else if(dfn[v] < low[u])
    20             low[u] = dfn[v];
    21         }
    22 
    23     }
    24     if(low[u] ==dfn[u])
    25     {
    26         bcc_cnt++;
    27         int t;
    28         do
    29         {
    30 
    31             t = s.top();
    32             s.pop();
    33             belong[t] = bcc_cnt;
    34         }
    35         while(t != u);
    36     }
    37 }
    38 
    39 
    40 void find_bcc(int n)
    41 {
    42     int i;
    43     memset(dfn,0,sizeof(dfn));
    44     memset(low,0,sizeof(low));
    45 
    46     memset(belong,0,sizeof(belong));
    47     while(!s.empty())s.pop();
    48     dfsclock = bcc_cnt = 0;
    49     loop(1,i,n)
    50     if(!dfn[i]) tarjan(i,-1);
    51     // puts("yes");
    52     // printf("%d  """"""
    ",bcc_cnt);
    53 }
    View Code

    点连通(好吧,这个代码其实是白书上的。明天自己写一个= =。太困了。。。)

     1 int dfs(int u,int fa)
     2 {
     3    int lowu= pre[u]=++dfsclock;
     4    int child=0;
     5     for(int i=0;i<g[u].size();i++)
     6     {
     7         int v=g[u][i];
     8         edge e;
     9         e.u=u,e.v=v;
    10         if(!pre[v])
    11         {
    12             st.push(e);
    13             child++;
    14             int lowv=dfs(v,u);
    15             lowu=min(lowu,lowv);
    16             if(lowv>=pre[u])
    17             {
    18                 iscut[u]=1;
    19                 bcc_cnt++;
    20                 bcc[bcc_cnt].clear();
    21                 for(;;)
    22                 {
    23                     edge x=st.top();st.pop();
    24                     if(bccno[x.u]!=bcc_cnt)
    25                     {
    26                         bcc[bcc_cnt].push_back(x.u);
    27                         bccno[x.u]=bcc_cnt;
    28                     }
    29                     if(bccno[x.v]!=bcc_cnt)
    30                     {
    31                         bcc[bcc_cnt].push_back(x.v);
    32                         bccno[x.v]=bcc_cnt;
    33                     }
    34                     if(x.u==u&&x.v==v)break;
    35                 }
    36             }
    37         }
    38         else if(pre[v]<pre[u]&&v!=fa)
    39         {
    40             st.push(e);
    41             lowu=min(lowu,pre[v]);
    42         }
    43     }
    44     if(fa<0&&child==1)iscut[u]=0;
    45     return lowu;
    46 }
    47 void find_bcc(int n)
    48 {
    49     int i;
    50     memset(pre,0,sizeof(pre));
    51     cl(iscut,0);
    52     cl(bccno,0);
    53     dfsclock = bcc_cnt = 0;
    54     loop(0,i,n)
    55     if(!pre[i]) dfs(i,-1);
    56    // puts("yes");
    57    // printf("%d  """"""
    ",bcc_cnt);
    58 }
    View Code

    过两天把这几次的题搞上去吧~

  • 相关阅读:
    Java Jaxb JavaBean与XML互转
    中国版Azure支持那些版本号Linux
    Codeforces 8D Two Friends 三分+二分+计算几何
    技术走向管理一些思考(3)-知识工作者的管理
    分析USB平台设备模型框架(1)
    (42)JS运动之多物体框架--多个div变宽
    听写英语单词的小程序
    一个基于node express4.0和mongodb的活动报名
    c编程:求出4&#215;4矩阵中最大和最小元素值及其所在行下标和列下标,求出两条主对角线元素之和。
    重载()运算符和重载强制类型转换
  • 原文地址:https://www.cnblogs.com/0803yijia/p/3251663.html
Copyright © 2011-2022 走看看