• 强连通分量初探 By cellur925


    并不理解。但是毕竟也做了一些题,略微小结。

    注:这里讨论的暂时是有向图的强联通分量。

    先贴出模板。学长:我也不理解,但我可以叫你们怎么背代码。

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<stack>
     4 #define maxn
     5 
     6 using namespace std;
     7 int dfn[maxn],low[maxn]
     8 
     9 void dfs(int p)
    10 {
    11     dfn[p]=low[p]=++dfs_clock;
    12     s.push(p);
    13     for(int i=head[p];i;i=edge[i].next)
    14     {
    15         int y=edge[i].to;
    16         if(!dfn[y])
    17         {
    18             dfs(y);
    19             low[p]=min(low[p],low[y]);
    20         }
    21         else if(!scc[y]) low[p]=min(low[p],dfn[y]);
    22     }
    23     if(dfn[p]==low[p])
    24     {
    25         scc_cnt++;
    26         while(1)
    27         {
    28             int x=s.top();
    29             s.pop();
    30             scc[x]=scc_cnt;
    31             if(x==p) break;
    32         }
    33     } 
    34 }
    35 
    36 int main()
    37 {
    38     scanf("%d",&n);
    39     for(int i=1;i<=m;i++)
    40      //读入边信息
    41     for(int i=1;i<=n;i++)
    42     {
    43         if(!dfn[i]) dfs(i);
    44         tong[scc[i]]++;
    45         //scc[i]->i点在哪个强连通分量
    46         //tong[i]-> 第i个强连通分量大小 
    47     }
    48     return 0;
    49 } 

    一 缩点

    一句话来说,就是求出有向图中的强联通分量后,把每个强联通分量用一个点代替,得到一个DAG(有向无环图)。

    我们用一个新的邻接表来记录新的DAG上的边。

    这个过程可以近似的理解为缩点。先放下求缩点的模板。

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<stack>
     4 #include<queue>
     5 #include<cstring>
     6 
     7 using namespace std;
     8 //tarjan_need
     9 int n,m,x,y,tot,qwq,ans,scc_cnt,dfs_clock,sum[10090],pv[10090],head[10090],head_DAG[10090],low[10090],dfn[10090],scc[10090];
    10 struct node{
    11     int next,to;
    12 }edge[100090];
    13 struct nodee{
    14     int next,to;
    15 }edge_DAG[100090];
    16 stack<int>s;
    17 
    18 void add(int x,int y,int op)
    19 {
    20     if(op==1)
    21     {
    22         edge[++tot].to=y;
    23         edge[tot].next=head[x];
    24         head[x]=tot;
    25     }
    26     else if(op==2)
    27     {
    28         edge_DAG[++qwq].to=y;
    29         edge_DAG[qwq].next=head_DAG[x];
    30         head_DAG[x]=qwq;
    31     }
    32 }
    33 
    34 void tarjan(int p)
    35 {
    36     dfn[p]=low[p]=++dfs_clock;
    37     s.push(p);
    38     for(int i=head[p];i;i=edge[i].next)
    39     {
    40         int y=edge[i].to;
    41         if(!dfn[y])
    42         {
    43             tarjan(y);
    44             low[p]=min(low[p],low[y]);
    45         }
    46         else if(!scc[y]) low[p]=min(low[p],dfn[y]);
    47     }
    48     if(dfn[p]==low[p])
    49     {
    50         scc_cnt++;
    51         while(1)
    52         {
    53             int x=s.top();
    54             s.pop();
    55             scc[x]=scc_cnt;
    56             if(x==p) break;
    57         }
    58     }
    59 }
    60 
    61 int main()
    62 {
    63     scanf("%d%d",&n,&m);
    64     for(int i=1;i<=n;i++) scanf("%d",&pv[i]);
    65     for(int i=1;i<=m;i++)
    66         scanf("%d%d",&x,&y),add(x,y,1);
    67     for(int i=1;i<=n;i++)
    68         if(!dfn[i]) tarjan(i);
    69     for(int x=1;x<=n;x++)
    70         for(int i=head[x];i;i=edge[i].next)
    71             {
    72                 int y=edge[i].to;
    73                 if(scc[x]!=scc[y]) 
    74                     add(scc[x],scc[y],2);
    75             }
    76     return 0;
    77 }

    然鹅洛谷的模板题更深一步,求:

    给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

    允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

    我们跑一遍tarjan进行缩点,得到一个新的DAG,在DAG上枚举起点,各跑一遍spfa求最长路,每跑完再枚举终点。注意这里是点带权而不是边带权,在进行强联通分量个数划分的时候,我们需要更新合并每个SCC的点权和(注意用新的数组保存!被这个地方卡了)。

    (其实还有一种缩点后dp的做法,用拓扑排序处理掉dp后效性

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<stack>
      4 #include<queue>
      5 #include<cstring>
      6 
      7 using namespace std;
      8 //tarjan_need
      9 int n,m,x,y,tot,qwq,ans,scc_cnt,dfs_clock,sum[10090],pv[10090],head[10090],head_DAG[10090],low[10090],dfn[10090],scc[10090];
     10 struct node{
     11     int next,to;
     12 }edge[100090];
     13 struct nodee{
     14     int next,to;
     15 }edge_DAG[100090];
     16 stack<int>s;
     17 //spfa_need
     18 int dis[10090];
     19 bool vis[10090];
     20 
     21 void add(int x,int y,int op)
     22 {
     23     if(op==1)
     24     {
     25         edge[++tot].to=y;
     26         edge[tot].next=head[x];
     27         head[x]=tot;
     28     }
     29     else if(op==2)
     30     {
     31         edge_DAG[++qwq].to=y;
     32         edge_DAG[qwq].next=head_DAG[x];
     33         head_DAG[x]=qwq;
     34     }
     35 }
     36 
     37 void tarjan(int p)
     38 {
     39     dfn[p]=low[p]=++dfs_clock;
     40     s.push(p);
     41     for(int i=head[p];i;i=edge[i].next)
     42     {
     43         int y=edge[i].to;
     44         if(!dfn[y])
     45         {
     46             tarjan(y);
     47             low[p]=min(low[p],low[y]);
     48         }
     49         else if(!scc[y]) low[p]=min(low[p],dfn[y]);
     50     }
     51     if(dfn[p]==low[p])
     52     {
     53         scc_cnt++;
     54         while(1)
     55         {
     56             int x=s.top();
     57             s.pop();
     58             scc[x]=scc_cnt;
     59             sum[scc_cnt]+=pv[x];
     60             if(x==p) break;
     61         }
     62     }
     63 }
     64 
     65 void spfa(int start)
     66 {
     67     memset(dis,0,sizeof(dis));
     68     memset(vis,0,sizeof(vis));
     69     queue<int>q;
     70     q.push(start);dis[start]=sum[start];vis[start]=1;
     71     while(!q.empty())
     72     {
     73         int x=q.front();q.pop();vis[x]=0;
     74         for(int i=head_DAG[x];i;i=edge_DAG[i].next)
     75         {
     76             int y=edge_DAG[i].to;
     77             if(dis[y]<dis[x]+sum[y])
     78             {
     79                 dis[y]=dis[x]+sum[y];
     80                 if(!vis[y])
     81                 {
     82                     vis[y]=1;
     83                     q.push(y);
     84                 }
     85             }
     86         }
     87     }
     88     for(int i=1;i<=scc_cnt;i++) ans=max(ans,dis[i]);
     89 }
     90 
     91 int main()
     92 {
     93     scanf("%d%d",&n,&m);
     94     for(int i=1;i<=n;i++) scanf("%d",&pv[i]);
     95     for(int i=1;i<=m;i++)
     96         scanf("%d%d",&x,&y),add(x,y,1);
     97     for(int i=1;i<=n;i++)
     98         if(!dfn[i]) tarjan(i);
     99     for(int x=1;x<=n;x++)
    100         for(int i=head[x];i;i=edge[i].next)
    101             {
    102                 int y=edge[i].to;
    103                 if(scc[x]!=scc[y]) 
    104                     add(scc[x],scc[y],2);
    105             }
    106     for(int i=1;i<=scc_cnt;i++) spfa(i);
    107     printf("%d",ans);
    108     return 0;
    109 }
    View Code

    二、受欢迎的牛

    题目描述

    每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶

    牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜

    欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你

    算出有多少头奶牛可以当明星。

    题意可以再简化: 给定一个有向图,问有多少个顶点是由任何顶点出发都可达的。

    分析:(抄lyc课件)

    • 有向无环图中唯一出度为 0 的点,一定可以由任何点出发均可达
    • 由于无环,所以从任何点出发往前走,必然终止于一个出度为0
    的点

    • 求出所有强连通分量,每个强连通分量缩成一点,形成一个有向
    无环图DAG。
    • DAG上面如果恰有一个出度为0的点,说明DAG所有的点可到达这
    个点,这个点是原图中的强连通分量,该强连通分量的点数,就
    是答案。
    • DAG上面如果有不止一个出度为0的点,因为它们不能互相到达,
    故原问题无解,答案为0

    code

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<stack>
     4  
     5 using namespace std;
     6 
     7 int n,m,tot,qwq,Chemist,cellur,dfs_clock,scc_cnt,qaq,qaqaq;
     8 int head[10090],head_DAG[10090],dfn[10090],low[10090],scc[10090],du[10090],tong[10090];
     9 struct node{
    10     int to,next;
    11 }edge[50090];
    12 stack<int>s;
    13 
    14 void add(int x,int y,int op)
    15 {
    16     if(op==1)
    17     {
    18         edge[++tot].to=y;
    19         edge[tot].next=head[x];
    20         head[x]=tot;
    21     }
    22 }
    23 
    24 void dfs(int p)
    25 {
    26     dfn[p]=low[p]=++dfs_clock;
    27     s.push(p);
    28     for(int i=head[p];i;i=edge[i].next)
    29     {
    30         int y=edge[i].to;
    31         if(!dfn[y])
    32         {
    33             dfs(y);
    34             low[p]=min(low[p],low[y]);
    35         }
    36         else if(!scc[y]) low[p]=min(low[p],dfn[y]);
    37     }
    38     if(dfn[p]==low[p])
    39     {
    40         scc_cnt++;
    41         while(1)
    42         {
    43             int x=s.top();
    44             s.pop();
    45             scc[x]=scc_cnt;
    46             if(x==p) break;
    47         }
    48     }
    49 }
    50 
    51 int main()
    52 {
    53     scanf("%d%d",&n,&m);
    54     for(int i=1;i<=m;i++)
    55         scanf("%d%d",&Chemist,&cellur),add(Chemist,cellur,1);
    56     for(int i=1;i<=n;i++)
    57     {
    58         if(!dfn[i]) dfs(i);
    59         tong[scc[i]]++;
    60     }
    61     for(int x=1;x<=n;x++)
    62         for(int i=head[x];i;i=edge[i].next)
    63             {
    64                 int y=edge[i].to;
    65                 if(scc[x]!=scc[y]) du[scc[x]]++;
    66             } 
    67     
    68     for(int i=1;i<=scc_cnt;i++)
    69          if(du[i]==0) qaq++,qaqaq=i;
    70          
    71     if(qaq==1) printf("%d",tong[qaqaq]);
    72     else printf("0");
    73     return 0;
    74 }
    View Code

    三、信息传递

    题目描述

    有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,

    其中,编号为 ii 的同学的信息传递对象是编号为 Ti 的同学。

    游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮?

    题意也是比较明白,求图中的最小环,我们可以用tarjan求(虽然有些小题大做的嫌疑)。记录各个强联通分量的大小,最后找最小的大于1的环就行了。

    code

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<stack>
     4 #define maxn 200090
     5 
     6 using namespace std;
     7 
     8 int n,y,tot,ans=999999,dfs_clock,scc_cnt,tong[maxn],dfn[maxn],low[maxn],head[maxn],scc[maxn]; 
     9 stack<int>s;
    10 struct node{
    11     int to,next;
    12 }edge[maxn];
    13 
    14 void add(int x,int y)
    15 {
    16     edge[++tot].to=y;
    17     edge[tot].next=head[x];
    18     head[x]=tot;
    19 }
    20 
    21 void dfs(int p)
    22 {
    23     dfn[p]=low[p]=++dfs_clock;
    24     s.push(p);
    25     for(int i=head[p];i;i=edge[i].next)
    26     {
    27         int y=edge[i].to;
    28         if(!dfn[y])
    29         {
    30             dfs(y);
    31             low[p]=min(low[p],low[y]);
    32         }
    33         else if(!scc[y]) low[p]=min(low[p],dfn[y]);
    34     }
    35     if(dfn[p]==low[p])
    36     {
    37         scc_cnt++;
    38         while(1)
    39         {
    40             int x=s.top();
    41             s.pop();
    42             scc[x]=scc_cnt;
    43             if(x==p) break;
    44         }
    45     }
    46 }
    47 
    48 int main()
    49 {
    50     scanf("%d",&n);
    51     for(int i=1;i<=n;i++)
    52      scanf("%d",&y),add(i,y);
    53     for(int i=1;i<=n;i++)
    54     {
    55         if(!dfn[i]) dfs(i);
    56         tong[scc[i]]++;
    57     }
    58     for(int i=1;i<=scc_cnt;i++) if(tong[i]>1&&tong[i]<ans) ans=tong[i];
    59     printf("%d
    ",ans);
    60     return 0;
    61 }
    View Code
  • 相关阅读:
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_03 斗地主案例(单列)_2_斗地主案例的代码实现
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_03 斗地主案例(单列)_1_斗地主案例的需求分析
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_02 泛型_6_泛型通配符
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_02 泛型_5_定义和使用含有泛型的接口
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_02 泛型_4_定义和使用含有泛型的方法
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_02 泛型_3_定义和使用含有泛型的类
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_02 泛型_2_使用泛型的好处
    阶段1 语言基础+高级_1-3-Java语言高级_04-集合_02 泛型_1_泛型的概念
    为什么企业编写代码时禁止使用制表符
    header('Location:'.C('VIP_HX').'/CmdId/'.$CmdId.'/user_id/'.$user_id.'/Token/'.$Token);
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9461586.html
Copyright © 2020-2023  润新知