zoukankan      html  css  js  c++  java
  • P1807 最长路 题解

    题目传送门

    一、理解与感悟

    有向图有负权边

    比如从1到n有两步,一步是权值是100,另一步权值是-100,如果初值设置为0,就搞不清楚是加起来之后是0,还是根本没办法到达。

    如果初值设置为-1,那么也是一个样,不知道是天生-1,还是运算完是-1。

    设置初值为INF是解决负权边的有效办法!!!

    二、vector方式建图解法

    #include <bits/stdc++.h>
    
    using namespace std;
    //拓扑排序+vector实现
    const int N = 50000 + 10;
    const int INF = 0x3f3f3f3f;
    
    //结构体
    struct Edge {
        int to;    //下一个结点
        int value; //边长
        int next;  //索引值
    };
    vector<Edge> p[N];
    
    int ind[N]; //入度
    int f[N];   //动态规划的结果
    queue<int> q;   //拓扑排序用的队列
    int n; //n个顶点
    int m; //m条边
    
    int main() {
        //读入
        cin >> n >> m;
        for (int i = 1; i <= m; ++i) {
            int u, v, w;//代表存在一条从 u 到 v 边权为 w 的边。
            cin >> u >> v >> w;
            p[u].push_back({v, w});
            ind[v]++;//统计入度个数
        }
        //入度为0的结点入队列,进行拓扑排序
        for (int i = 1; i <= n; i++) if (!ind[i]) q.push(i);
    
        //初始化,将到达节点1的距离设为最大值,这个用的太妙了~~
        //防止出现负权边,也防止出现了0不知道是权边加在一起出现的,还是天生就是0
        //调高起点值看来是解决负权边的重要方法,学习学习。
        f[1] = INF;
    
        //拓扑排序
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            //遍历每条出边
            for (int i = 0; i < p[u].size(); i++) {
                int y = p[u][i].to;
                --ind[y]; //在删除掉当前结点带来的入度
                //精髓!~
                //运用拓扑排序的思想,对每个节点进行访问,并在此处用上动态规划的思路更新到此节点的最大距离
                f[y] = max(f[y], f[u] + p[u][i].value); //利用走台阶思路,从上一级走过来
                if (!ind[y]) q.push(y);//在删除掉当前结点带来的入度后,是不是入度为0了,如果是将点y入队列
            }
        }
        //如果可以到达,则输出最长路径
        if (f[n] > 0)printf("%d", f[n] - INF);
        else printf("-1");
        return 0;
    }
    

    三、链式前向星建图解法

    #include <bits/stdc++.h>
    
    using namespace std;
    //拓扑排序+链接式前向星实现
    const int N = 50000 + 10;
    const int INF = 0x3f3f3f3f;
    
    //链式前向星
    struct Edge {
        int to;    //下一个结点
        int value; //边长
        int next;  //索引值
    } edge[N];     //边数,也不可能多于结点数,因为这里是指每个结点引出的边数集合
    
    int idx;    //索引下标
    int head[N];//拉链的链表
    int ind[N]; //入度
    int f[N];   //动态规划的结果
    
    queue<int> q;   //拓扑排序用的队列
    
    //加入一条边,x起点,y终点,value边权
    void add_edge(int x, int y, int value) {
        edge[++idx].to = y;
        edge[idx].value = value;
        edge[idx].next = head[x];
        head[x] = idx;
    }
    
    int n; //n个顶点
    int m; //m条边
    
    int main() {
        //读入
        cin >> n >> m;
        for (int i = 1; i <= m; ++i) {
            int u, v, w;//代表存在一条从 u 到 v 边权为 w 的边。
            cin >> u >> v >> w;
            add_edge(u, v, w);
            ind[v]++;//统计入度个数
        }
        //入度为0的结点入队列,进行拓扑排序
        for (int i = 1; i <= n; i++) if (!ind[i]) q.push(i);
    
        //初始化,将到达节点1的距离设为最大值,这个用的太妙了~~
        //防止出现负权边,也防止出现了0不知道是权边加在一起出现的,还是天生就是0
        //调高起点值看来是解决负权边的重要方法,学习学习。
        f[1] = INF;
    
        //拓扑排序
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            //遍历每条出边
            for (int i = head[u]; i; i = edge[i].next) {
                int y = edge[i].to;
                --ind[y]; //在删除掉当前结点带来的入度
                //精髓!~
                //运用拓扑排序的思想,对每个节点进行访问,并在此处用上动态规划的思路更新到此节点的最大距离
                f[y] = max(f[y], f[u] + edge[i].value); //利用走台阶思路,从上一级走过来
                if (!ind[y]) q.push(y);//在删除掉当前结点带来的入度后,是不是入度为0了,如果是将点y入队列
            }
        }
        //如果可以到达,则输出最长路径
        if (f[n] > 0)printf("%d", f[n] - INF);
        else printf("-1");
        return 0;
    }
    
  • 相关阅读:
    咏码农
    IDisposable 的 Visual C# 模板
    PDF补丁丁(0.3.2.13测试版)新增光学字符识别(OCR)功能,将图片 PDF 文档的目录转换为书签
    PDF 补丁丁开发计划列表及已知问题
    PDF补丁丁0.3.3.9测试版(新增 PDF 文档结构探查器)
    PDF补丁丁(0.3.2.14测试版)优化提取PDF页面功能,删除PDF页面更方便
    PDF补丁丁(0.3.2.1测试版)新增光学字符识别(OCR)功能,识别 PDF 文档图片的文字
    PDF 补丁丁0.3.2 即将发布正式版
    PDF 补丁丁0.3.2版正式发布(新增PDF OCR,加强自动生成书签、书签编辑器等功能)
    PDF 补丁丁 0.3.3.10 测试版(新增结构探查器可编辑 PDF 文档)
  • 原文地址:https://www.cnblogs.com/littlehb/p/15127505.html
Copyright © 2011-2022 走看看