zoukankan      html  css  js  c++  java
  • 做题笔记 图的遍历 P3916

      今天饭后刷题,突然看见自己任务清单里面有这么一题,就打算开写。

      题面说的是有向图的遍历(n <= 10^5,m <= 10^5),很自然的想到了dfs,使用vector存图,我想到了两种dfs方法:

    • 对于每个点进行dfs,每一次dfs遍历可以达到的所有点,求出最大值
      • 很暴力,方法也很简单,但是问题是,最坏情况下每一个点都需要遍历全图,时间复杂度为O(nm)
    • 从大到小对于点进行dfs,对于每一个遍历的点,如果没有就可以把这个点的最大值设为它。
      • 这个方法相对与上一个来说有了非常大的优化,循环从N到1,则每个点i能访问到的结点的A值都是i,每个点访问一次,这个A值就是最优的,因为之后如果再访问到这个结点那么答案肯定没当前大了。这个方法的时间复杂度O(n+m)(吧)

      代码实现:

     1 #include <cstdio>
     2 #include <cctype>
     3 #include <vector>
     4 #define MAXN 100010
     5 #define _for(i,a,b) for (int i = a;i <= b;i++)
     6 #define _fd(i,a,b) for (int i = b;i >= a;i--)
     7 using namespace std;
     8 
     9 vector <int> G[MAXN];
    10 int n,m,a[MAXN];
    11 
    12 inline int read() {
    13     int a = 0,f = 1;
    14     char v= getchar();
    15     while (!isdigit(v)) {
    16         if (v == '-') {
    17             f = -1;
    18         }
    19         v = getchar();
    20     }
    21     while (isdigit(v)) {
    22         a = a * 10 + v - 48;
    23         v = getchar();
    24     }
    25     return a * f;
    26 }
    27 
    28 inline void addedge(int u,int v) {
    29     G[v].push_back(u);
    30 }
    31 
    32 void dfs(int x,int p) {
    33     if (a[x]) {
    34         return ;
    35     }
    36     a[x] = p;
    37     int vv = G[x].size();
    38     for (int i = 0;i < vv;i++) {
    39         dfs(G[x][i],p);
    40     }
    41 }
    42 
    43 int main() {
    44     n = read(),m = read();
    45     _for(i,1,m) {
    46         int u = read(),v = read();
    47         addedge(u,v);
    48     }
    49     _fd(i,1,n) {
    50         dfs(i,i);
    51     }
    52     _for(i,1,n) {
    53         printf("%d ",a[i]);
    54     }
    55     return 0;
    56 }

      后来,我翻看题解的时候,猛然发现Tarjan也能解!

      使用Tarjan求出联通分量,每一个联通分量里面的点的答案都是一样的!

      代码实现(题解):

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 using namespace std;
     6 
     7 const int maxn = 10e5 + 5;
     8 
     9 struct Edge{
    10     int to,next;
    11 }e[maxn];
    12 
    13 int dfn[maxn],low[maxn],Time;
    14 int s[maxn],top,vis[maxn];
    15 int f[maxn],n,m;
    16 int cnt,belong[maxn],MAX[maxn];
    17 int k,x[maxn],y[maxn],head[maxn];
    18 
    19 void add(int u,int v)
    20 {
    21     e[++k].to = v;
    22     e[k].next = head[u];
    23     head[u] = k;
    24 }
    25 
    26 void tarjan(int x)                   //求有向图强联通分量的tarjan,在这里不过多叙述,想学习的可以点链接去博客qwq 
    27 {
    28     vis[x] = 1;
    29     s[++top] = x;
    30     dfn[x] = low[x] = ++Time;
    31     for(int i = head[x];i;i=e[i].next)
    32     {
    33         int to = e[i].to;
    34         if(!dfn[to])
    35         {
    36             tarjan(to);
    37             low[x] = min(low[x],low[to]);
    38         }
    39         else if(vis[to])
    40             low[x] = min(low[x],dfn[to]);
    41     }
    42     if(dfn[x] == low[x])            //如果x及其子树能够构成一个强联通分量 
    43     {
    44         ++cnt;
    45         int j;
    46         while(j=s[top])             //将其子树中的点都加入此分量中,并将此分量中的最大值保存在MAX数组中 
    47         {
    48             vis[j] = 0;
    49             belong[j] = cnt;
    50             MAX[cnt] = max(MAX[cnt],j);
    51             top--;
    52             if(j==x) break;
    53         }
    54     }
    55 }
    56 
    57 void dfs(int x)                     //记忆化搜索 
    58 {
    59     if(f[x]) return;
    60     f[x] = MAX[x];                  //当前强联通分量中的最大值 
    61     for(int i=head[x];i;i=e[i].next)
    62     {
    63         int to = e[i].to;
    64         if(!f[to]) dfs(to);
    65         f[x] = max(f[x],f[to]);     // 子树中的最大值 
    66     }
    67 }
    68 
    69 int main()
    70 {
    71     //freopen("data.in","r",stdin);
    72     //freopen("jkb.out","w",stdout);
    73     scanf("%d%d",&n,&m);
    74     for(int i=1;i<=m;i++)
    75     {
    76         scanf("%d%d",&x[i],&y[i]);          //保存边信息 
    77         add(x[i],y[i]);
    78     }
    79     for(int i=1;i<=n;i++)                   //求出强联通分量 
    80         if(!dfn[i])
    81             tarjan(i);
    82     memset(e,0,sizeof(e));                  //清空原图 
    83     memset(head,0,sizeof(head));    
    84     k = 0;
    85     for(int i=1;i<=m;i++)                   //建立新图 
    86         if(belong[x[i]]!=belong[y[i]])
    87             add(belong[x[i]],belong[y[i]]);
    88     for(int i=1;i<=cnt;i++)
    89         if(!f[i]) dfs(i);
    90     for(int i=1;i<=n;i++)                   //输出答案 
    91         printf("%d ",f[belong[i]]);
    92     return 0;
    93 }
  • 相关阅读:
    js实现分享到QQ
    js 复制粘贴
    js弹窗 js弹出DIV,并使整个页面背景变暗
    PHP实现大转盘抽奖算法
    ext 树节点操作
    ExtJS4图片验证码的实现
    随笔分类
    Oracle、MySql、SQLServer 数据分页查询
    Repeater控件使用(含删除,分页功能)
    SQL compute by 的使用
  • 原文地址:https://www.cnblogs.com/doubeecat/p/10333714.html
Copyright © 2011-2022 走看看