• Luogu P2656 采蘑菇


    尽管是缩点的习题,思路也是在看了题解后才明白的。

    首先,每个强连通分量内的点都是一定互通的,也就是可以完全把这里面的边都跑满,摘掉所有能摘的蘑菇。那么,考虑给每一个强连通分量化为的新点一个点权,代表摘光蘑菇能拿到的边权之和。然后,在新点之间保留原来的桥及其初始权值。(每一个桥一定只能跑一遍,否则说明这两个本应单向通行的分量之间有返回的路径,则二者可构成一个更大的分量。这个结论正是tarjan算法求有向图dcc的核心原理。)现在得到了一张新图,问题在于如何在一张包含点权、边权的DAG上求起始于定点的最长路。

    这个问题可以用拓扑序DP求解。在dp求最长路的基础上,为了保证一定由s点所在分量起始,我们把该分量初状态设为其权值,其余点都赋初值为INT_MIN。

    这样dp得到的最长路一定是基于f[dcc[s]]求出的。

    另外,用SPFA算法来跑点权、边权交叉的最长路是可行的,不过应用于本题复杂度不如dp优秀。-----------------------------------------

    在参阅题解后,基于一开始跑偏的假设,笔者又想到了一个貌似更优的解法。

    实际上我们并不需要考虑原图的所有节点。容易想到,从给定起点向外作一轮tarjan算法(dfs)不能达到的点,在新图中也不可能走到。因此,我们只需要对图中以s为原点作一次tarjan能够跑到的几个连通分量进行缩点,这样能够到达的区域就变成了一棵以s为根的树(8月20日订正:这里的“树”更严谨的说法是“树形图”)。我们只需要再作一次dfs求出最深叶节点的深度即可。

    (注:以下代码注释分部分为除最后一种思路的其余解法,仅供参考)

    1. #include <cstdio>  
    2. #include <iostream>  
    3. #include <queue>  
    4. #include <climits>  
    5. #define rint register int  
    6. #define BUG putchar('*')  
    7. #define maxn 80010  
    8. #define maxm 200010  
    9. using namespace std;  
    10.   
    11. struct E {  
    12.     int to, nxt, w;  
    13.     double op;  
    14. } edge[maxm], edge2[maxm];  
    15. int n, m, st;  
    16. int head[maxn], top;  
    17. inline void insert(int u, int v, int w, double op) {  
    18.     edge[++top] = (E) {v, head[u], w, op};  
    19.     head[u] = top;  
    20. }  
    21. int dfn[maxn], low[maxn], sta[maxn], stp, timer;  
    22. bool ins[maxn], vis[maxn];  
    23. int cnt, c[maxn];  
    24. void dfs(int u) {  
    25.     dfn[u] = low[u] = ++timer;  
    26.     sta[++stp] = u;  
    27.     ins[u] = true;  
    28.     vis[u] = true;//  仅搜一次标记所答点
    29.     for (rint i = head[u]; i; i = edge[i].nxt) {  
    30.         int v = edge[i].to;  
    31.         if (!dfn[v]) {  
    32.             dfs(v);  
    33.             low[u] = min(low[u], low[v]);  
    34.         } else if (ins[v])  
    35.             low[u] = min(low[u], dfn[v]);  
    36.     }  
    37.     if (dfn[u] == low[u]) {  
    38.         ++cnt;  
    39.         int x;  
    40.         do {  
    41.             x = sta[stp--];  
    42.             ins[x] = false;  
    43.             c[x] = cnt;  
    44.         } while (x != u);  
    45.     }  
    46. }  
    47. void tarjan() {  
    48. //  for (int i = 1; i <= n; ++i) //  全图tarjan
    49. //      if (!dfn[i]) dfs(i);  
    50.     dfs(st);  
    51. }  
    52. int head2[maxn], top2;  
    53. inline void insert2(int u, int v, int w) {  
    54.     edge2[++top2] = (E) {v, head2[u], w, 0};  
    55.     head2[u] = top2;  
    56. }  
    57. int val[maxn], ind[maxn];  
    58. void build() {  
    59.     rint v, w;  
    60.     for (rint u = 1; u <= n; ++u)  
    61.         if (vis[u])//  仅考虑一次搜索 缩点得树
    62.         for (int i = head[u]; i; i = edge[i].nxt) {  
    63.             v = edge[i].to;  
    64.             w = edge[i].w;  
    65.             if (c[u] == c[v]) {  
    66.                 register double op = edge[i].op;  
    67.                 while (w)   
    68.                     val[c[u]] += w, w *= op;  
    69.             } else   
    70.                 insert2(c[u], c[v], w), ind[c[v]]++;  
    71.         }  
    72. }  
    73. //************************  
    74. /*  DAG 拓扑序dp
    75. int f[maxn];  
    76. queue<int> q;  
    77. int dp() {  
    78.     int ans = val[c[st]];  
    79.     for (int i = 1; i <= cnt; ++i) {  
    80.         f[i] = INT_MIN;  
    81.         if (!ind[i]) q.push(i);  
    82.     }  
    83.     f[c[st]] = val[c[st]];  
    84.     while (!q.empty()) {  
    85.         int u = q.front(); q.pop();  
    86.         for (int i = head2[u]; i; i = edge2[i].nxt) {  
    87.             int v = edge2[i].to;  
    88.             f[v] = max(f[v], f[u] + edge2[i].w + val[v]);  
    89.             --ind[v];  
    90.             if (!ind[v])   
    91.                 ans = max(ans, f[v]), q.push(v);  
    92.         }  
    93.     }  
    94.     return ans;  
    95. }  
    96. */  
    97. //**************************  
    98. /*  spfa
    99. bool inq[maxn];  
    100. int dist[maxn];  
    101. int spfa() {  
    102.     for (int i = 1; i <= cnt; ++i)  
    103.         dist[i] = INT_MIN;  
    104.     dist[c[st]] = val[c[st]];  
    105.     queue<int> q;  
    106.     inq[c[st]] = true, q.push(c[st]);  
    107.     while (!q.empty()) {  
    108.         int u = q.front();  
    109.         q.pop(), inq[u] = false;  
    110.         for (int i = head2[u]; i; i = edge2[i].nxt) {  
    111.             int v = edge2[i].to;  
    112.             if (dist[v] < dist[u] + edge2[i].w + val[v]) {  
    113.                 dist[v] = dist[u] + edge2[i].w + val[v];  
    114.                 if (!inq[v])  
    115.                     q.push(v), inq[v] = true;  
    116.             }  
    117.         }  
    118.     }  
    119.     int ans = 0;  
    120.     for (int i = 1; i <= cnt; ++i)  
    121.         ans = max(ans, dist[i]);  
    122.     return ans;  
    123. }*/  
    124. //***************************  
    125. int ans;  
    126. void dfs2(int u, int dist) {  
    127.     dist += val[u];  
    128.     if (!head2[u]) {  
    129.         ans = max(ans, dist);  
    130.         return;  
    131.     }  
    132.     for (int i = head2[u]; i; i = edge2[i].nxt)  
    133.         dfs2(edge2[i].to, dist + edge2[i].w);  
    134. }  
    135. int main() {  
    136.     scanf("%d %d", &n, &m);  
    137.     int u, v, w;  
    138.     double op;  
    139.     for (rint i = 1; i <= m; ++i) {  
    140.         scanf("%d %d %d %lf", &u, &v, &w, &op);  
    141.         insert(u, v, w, op);  
    142.     }  
    143.     scanf("%d", &st);  
    144.     tarjan();  
    145.     build();  
    146. //  printf("%d", spfa());  
    147. //  printf("%d", dp());  
    148.     dfs2(c[st], 0);  
    149.     printf("%d", ans);  
    150.     return 0;  
    151. }  

     这个题最大的收获是发现有向图缩点总跟DAG上的topo+DP有联系。按拓扑序遍历到某一点u,意味u点所有的入点都已经对其完成了更新,此时u点的状态满足无后效性。以及在DAG上求解始于某点的最长路径时,对f数组的特殊处理。

  • 相关阅读:
    知识工程及语义网技术 2020-03-19 (第一节)-构建本体
    知识工程及语义网技术 2020-03-12 (第二节)-构建本体
    知识工程及语义网技术 2020-03-12 (第二节)、RDF(S)、OWL
    知识工程及语义网技术(一)-XML、RDF(S)、OWL-2020.3.5第一节
    知识工程及语义网技术(一)-知识工程,万维网、语义网、本体工程 2020-02-20 (第一节)
    本体
    语义网技术及其应用(四)-欧石燕
    一文深度揭秘3GPP:2G/3G/4G/Pre-5G标准化制定流程是这样的
    3GPP更新5G标准时间表
    一张图了解3GPP
  • 原文地址:https://www.cnblogs.com/TY02/p/11107925.html
Copyright © 2020-2023  润新知