zoukankan      html  css  js  c++  java
  • [luogu p1807] 最长路

    传送门

    最长路

    题目描述

    (G) 为有 (n) 个顶点的带权有向无环图,(G) 中各顶点的编号为 (1)(n),请设计算法,计算图 (G)(<1,n>) 间的最长路径。

    输入输出格式

    输入格式

    输入的第一行有两个整数,分别代表图的点数 (n) 和边数 (m)

    (2) 到第 ((m + 1)) 行,每行 (3) 个整数 (u, v, w),代表存在一条从 (u)(v) 边权为 (w) 的边。

    输出格式

    输出一行一个整数,代表 (1)(n) 的最长路。

    (1)(n) 不联通,请输出 (-1)

    输入输出样例

    输入样例 #1

    2 1
    1 2 1
    

    输出样例 #1

    1
    

    说明

    数据规模与约定

    • 对于 (20\%)的数据,(n leq 100)(m leq 10^3)
    • 对于 (40\%) 的数据,(n leq 10^3)(m leq 10^{4})
    • 对于 (100\%) 的数据,(1 leq n leq 1500)(1 leq m leq 5 imes 10^4)(1 leq u, v leq n)(-10^5 leq w leq 10^5)

    分析

    此题有两种解法。一是最短路魔改,二是拓扑排序。

    最短路魔改这个解法相信很简单,用支持负图的最短路算法(比如SPFA,BF等),将所有边权值取反,跑最短路,然后结果再取反即可。当然不支持负图的最短路算法,比如dij,也可以通过更改一些内容来实现最长路,这些都是很简单的,不再细说。

    拓扑排序解法也很简单。定义 (dis_i)(1 ightarrow i) 的最短路径, (ind_i)(i) 节点的入度,(flag_i)(i) 节点是否与 (1) 节点连通。算法如下:

    1. 设置 (ind)(dis_n) 设置为 (-1)(flag_1) 设置为true。
    2. 将所有满足 (ind_i = 0)(i) push进队列 (q)
    3. 若队列非空,取队列第一个作为节点 (u),并pop掉该节点。
    4. 遍历 (u) 指向的点 (v),令 (ind_v - 1 ightarrow ind_v)
    5. (flag_u) 为true,且 (dis_v < dis_u + w)(w)(u ightarrow v) 这条边的权值),那么令 (dis_v = dis_u + w)。(最长路核心步骤)
    6. 另外,如果 (flag_u) 为true,那么 (flag_v) 也要设置为 true。
    7. 如果 (ind_v = 0),将 (v) 节点入队 (q)
    8. 重复 (3 sim 7) 步,直到 (q) 队列为空。此时的 (dis_n) 即为 (1 ightarrow n) 的最长路。

    这个算法的正确性也挺显然的。如果 (ind_u = 0),那么 (dis_u) 就会变为定值。

    那就上代码吧。

    代码

    /*
     * @Author: crab-in-the-northeast 
     * @Date: 2020-09-25 21:24:06 
     * @Last Modified by: crab-in-the-northeast
     * @Last Modified time: 2020-09-26 11:36:28
     */
    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <vector>
    
    const int maxn = 1505;
    const int maxm = 50005;
    
    struct edge {
        int u, v, dis;
    };
    
    int n, m;
    std :: vector <edge> G[maxn];
    int ind[maxn], dis[maxn];
    bool flag[maxn];
    
    void topsort() {
        flag[1] = true;
        std :: queue <int> q;
        for (int i = 1; i <= n; ++i)
            if (ind[i] == 0)
                q.push(i);
        
        while (!q.empty()) {
            int u = q.front();
            q.pop();
    
            for (int i = 0; i < G[u].size(); ++i) {
                int v = G[u][i].v;
                --ind[v];
                if (flag[u]) {
                    if (dis[v] < dis[u] + G[u][i].dis)
                        dis[v] = dis[u] + G[u][i].dis;
                    flag[v] = true;
                }
    
                if (ind[v] == 0)
                    q.push(v);
            }
        }
    }
    
    int main() {
        std :: scanf("%d %d", &n, &m);
        for (int i = 1; i <= m; ++i) {
            edge now;
            std :: scanf("%d %d %d", &now.u, &now.v, &now.dis);
            G[now.u].push_back(now);
            ++ind[now.v];
        }
    
        dis[n] = -1;
        topsort();
        std :: printf("%d
    ", dis[n]);
        return 0;
    }
    

    番外:一个错误的解法

    有些人可能会说,啊东北小蟹蟹,你一开始就把所有的 (ind_i = 0)(i) 都入队了,这是对的,但是感觉有点浪费,如果我把所有满足 (ind_i = 0)(i eq 1) 的节点的出边都砍掉,然后直接从1开始,不会很省时间吗?

    但实际上,这个解法是错误的。来举一个反例:

    0P3niQ.png

    如果按照这个算法,一开始是先删除了 (3 ightarrow 2) 这条边,然后进入拓扑,(4 ightarrow 5)。到这里都是正常的,但是从 (5) 开始出现了问题。因为有 (2 ightarrow 5) 这条边,所以 (ind_5 eq 0),因此 (5) 没有入队,于是算法提前迷之结束了,也就是说算法认为 (1 ightarrow 6) 不连通。离谱吧?

    但是,这个算法还真的可过这道题……为什么呢?

    蟹蟹的推测是,数据是按照(u ightarrow v in G),那么 (u < v) 来造的。如果数据一定满足这个条件,那么这个算法就是没问题的。这又是为什么呢?

    再来举一个和刚刚非常相似的例子。

    0P8lfH.png

    区别在哪里?没错,(2)(3) 序号颠倒了。你可能想问了,这和刚刚有什么区别啊?算法不还是认为 (1 ightarrow 6) 不连通吗?

    你想,我从 (2 ightarrow n) 删边,那么我首先删了 (2 ightarrow 3) 这条边,那么 (ind_3) 变为了 (0)。如下图:

    0P80hQ.png

    此时开始到 (3) 号节点。发现 (ind_3 = 0),因此算法会把 (3 ightarrow 5) 这条边删掉。如下:

    0P8z3d.png

    此时再运行拓扑排序,答案就是正常的,为 (8)。而最开始的那张图呢?

    0P3niQ.png

    算法会先尝试处理 (2)发现 (ind_2 eq 0),因此没有任何变化,转而处理 (3),删掉了 (3 ightarrow 2) 这条边。但是 (2 ightarrow 5) 这条边并没有成功删除,因此 (5) 节点变成了死点

    评测记录

    评测记录

  • 相关阅读:
    JVM(7) Java内存模型与线程
    JVM(6) 字节码执行引擎
    JVM(5) 类加载机制
    JVM(4) 类文件结构
    JVM(3) 垃圾收集器与内存分配策略
    python的with
    python http server handle json
    c++文件读写
    python字符串处理
    python decorator
  • 原文地址:https://www.cnblogs.com/crab-in-the-northeast/p/luogu-p1807.html
Copyright © 2011-2022 走看看