• 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;
    }
    
  • 相关阅读:
    从csv文件里取数据作为请求参数,和把返回数据放到一个csv文件
    记一次if控制器的使用
    记一次使用正则表达式+foreach控制器调试
    获取随机数用作入参使用
    获取返回结果作为参数并将其设置为全局变量(实现跨线程组使用)
    linux默认的目录结构
    总结fiddler抓https包
    Codeforces Round #733 (Div. 1 + Div. 2) D. Secret Santa
    Codeforces Round #733 (Div. 1 + Div. 2) C. Pursuit
    Codeforces Round #731 (Div. 3) A
  • 原文地址:https://www.cnblogs.com/littlehb/p/15127505.html
Copyright © 2020-2023  润新知