zoukankan      html  css  js  c++  java
  • tarjan【模板】缩点

    传送门:https://www.luogu.org/problemnew/show/P3387

    首先呢,tarjan找一个图的强连通分量是基于对图的dfs的。这中间开了一个dfn[]代表dfs序,还有个low[]代表该节点在dfs形成的树中能到达的最近的根。然后分情况进行更新(一会儿看我代码吧)。

    为了记录一个强联通分量,我们还要在开一个栈来储存当前查找的强连通分量。如果low[x] == dfn[x],那就说明这个点在dfs树种不能往上再爬了,于是就开始弹栈。

     

    对于这道题呢,先用tarjan找出强联通分量,然后缩点建立新图。可以发现,建立的新图是一个拓扑图,所以可以拓扑排序后在图上dp,然而我这么写wa了。翻题解的时候看到了一个更简单的方法:记录每一个点的入度,对于每一个入度为0的点用spfa跑一遍最长路,然后在所有最长路中取一个max即可。

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<cctype>
      7 #include<vector>
      8 #include<stack>
      9 #include<queue>
     10 using namespace std;
     11 #define enter printf("
    ")
     12 #define space printf(" ")
     13 #define Mem(a) memset(a, 0, sizeof(a))
     14 typedef long long ll;
     15 typedef double db;
     16 const int INF = 0x3f3f3f3f;
     17 const db eps = 1e-8;
     18 const int maxn = 1e5 + 5;
     19 inline ll read()
     20 {
     21     ll ans = 0;
     22     char ch = getchar(), last = ' ';
     23     while(!isdigit(ch)) {last = ch; ch = getchar();}
     24     while(isdigit(ch))
     25     {
     26         ans = ans * 10 + ch - '0'; ch = getchar();    
     27     }
     28     if(last == '-') ans = -ans;
     29     return ans;
     30 }
     31 inline void write(ll x)
     32 {
     33     if(x < 0) x = -x, putchar('-');
     34     if(x >= 10) write(x / 10);
     35     putchar('0' + x % 10);
     36 }
     37 
     38 vector<int> v[maxn];
     39 int n, m, a[maxn];
     40 
     41 int dfn[maxn], low[maxn], cnt = 0;
     42 bool in[maxn];
     43 stack<int> st;
     44 int col[maxn], ccol = 0, val[maxn];
     45 void tarjan(int now)
     46 {
     47     dfn[now] = low[now] = ++cnt;
     48     st.push(now); in[now] = 1;
     49     for(int i = 0; i < (int)v[now].size(); ++i)
     50     {
     51         if(!dfn[v[now][i]])                    //这个联通块还没找完 
     52         {
     53             tarjan(v[now][i]);
     54             low[now] = min(low[now], low[v[now][i]]);
     55         }
     56         else if(in[v[now][i]]) low[now] = min(low[now], dfn[v[now][i]]);    //这个点找到了他所在联通块的祖先节点 
     57     }
     58     if(low[now] == dfn[now])        //开始找出联通块中的所有点 
     59     {
     60         int x; ccol++;            //ccol相当于染色种数,也就是有多少个连通分量 
     61         do
     62         {
     63             x = st.top();
     64             in[x] = 0; st.pop();
     65             col[x] = ccol; val[ccol] += a[x];    //val[]记录新图上的点的点权 
     66         }while(x != now);
     67     }
     68     return;
     69 }
     70 
     71 vector<int> v2[maxn];
     72 bool du[maxn];
     73 void newGraph(int now)
     74 {
     75     for(int i = 0; i < (int)v[now].size(); ++i)
     76     {
     77         int x = col[now], y = col[v[now][i]];
     78         if(x == y) continue;        //别忘了两个点在一个联通块的情况 
     79         v2[x].push_back(y);
     80         du[y] = 1;
     81     }
     82     return;
     83 }
     84 
     85 
     86 int ans = 0;
     87 
     88 int dis[maxn];
     89 bool vis[maxn];
     90 void spfa(int s)
     91 {
     92     Mem(vis);
     93     for(int i = 1; i <= ccol; ++i) dis[i] = -INF;
     94     queue<int> q;
     95     q.push(s); vis[s] = 1;
     96     dis[s] = val[s];
     97     while(!q.empty())
     98     {
     99         int now = q.front(); q.pop(); vis[now] = 0;
    100         for(int i = 0; i < (int)v2[now].size(); ++i)
    101         {
    102             if(dis[now] + val[v2[now][i]] > dis[v2[now][i]])
    103             {
    104                 dis[v2[now][i]] = dis[now] + val[v2[now][i]];
    105                 if(!vis[v2[now][i]]) {q.push(v2[now][i]); vis[v2[now][i]] = 1;}
    106             }
    107         }
    108     }    
    109     for(int i = 1; i <= ccol; ++i) ans = max(ans, dis[i]);        //更新答案 
    110 }
    111 int main()
    112 {
    113     n = read(); m = read();
    114     for(int i = 1; i <= n; ++i) a[i] = read();
    115     for(int i = 1; i <= m; ++i)
    116     {
    117         int x = read(), y = read();
    118         v[x].push_back(y);
    119     }
    120     for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i);        //有的点从一个定点出发可能走不到,就都得判断是否走过 
    121     for(int i = 1; i <= n; ++i) newGraph(i);                //建立新图 
    122     for(int i = 1; i <= ccol; ++i) if(!du[i]) spfa(i);        //对于每一个入度为0的点,跑最长路 
    123     write(ans); enter;
    124     return 0;
    125 }
  • 相关阅读:
    软件工程的国家标准下载链接
    电子计算机机房设计规范
    建筑物防雷设计规范
    信息系统项目管理师考试大纲
    计算机信息系统安全保护等级划分准则
    信息系统工程监理单位资质管理办法
    信息系统工程监理工程师资格管理办法
    计算机软件保护条例
    信息系统工程监理暂行规定
    第一个Winform 程序 (附一个需求实现,望大家帮忙)
  • 原文地址:https://www.cnblogs.com/mrclr/p/9404994.html
Copyright © 2011-2022 走看看