zoukankan      html  css  js  c++  java
  • P1462 通往奥格瑞玛的道路

    主要说说这波卡常的感受.

    求交费最多的一次的最小值.使用二分法.

    即二分金额x,每次检查能否在忽略费用超过x的城市的情况下到达终点的最短路径长度是否小于血量b.由于没有负权边,采用Dijkstra.复杂度是O((n+m)logm log fmax),大概是107,勉强可以过.

    实现起来没有什么难度,然而,被卡常了,本地第一个数据一直跑1.1s+.

    那就只能走歪门斜路了,加个快读,开个O2,迫不得已又加个register,还是没能冲过去.STL常数是大,但我这么懒是不可能去学链式向前星的,太丑了.

    最后发现在Dijkstra里有个地方可以剪枝:

        while (!q.empty()) {
            E cur = q.top();
            q.pop();
            if (used[cur.to]) continue;
    
            used[cur.to] = true;
            dist[cur.to] = cur.wei;
            if (cur.to == n) break;    // 加上这个剪枝
            for (auto i : e[cur.to])
                if (f[i.to] <= x) q.push({i.to, i.wei + cur.wei});
        }

    效果比卡常高到不知道哪去了.

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <vector>
    using namespace std;
    
    struct E {
        int to;
        long long wei;
        bool operator<(const E &other) const { return wei > other.wei; }
    };
    vector<E> e[10010];
    int n, m, b, f[10010];
    long long dist[10010];
    bool used[10010];
    
    inline int read() {
        char ch = getchar();
        int x = 0, f = 1;
        while (ch > '9' || ch < '0') {
            if (ch == '-') f = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9') {
            x = x * 10 + ch - '0';
            ch = getchar();
        }
        return x * f;
    }
    
    bool check(int x) {  // under x money
        fill(dist, dist + n + 1, 1e15);
        fill(used, used + n + 1, 0);
    
        priority_queue<E> q;
        if (f[1] <= x) q.push({1, 0});
        while (!q.empty()) {
            E cur = q.top();
            q.pop();
            if (used[cur.to]) continue;
    
            used[cur.to] = true;
            dist[cur.to] = cur.wei;
            if (cur.to == n) break;
            for (auto i : e[cur.to])
                if (f[i.to] <= x) q.push({i.to, i.wei + cur.wei});
        }
    
        return dist[n] < b;
    }
    
    int main() {
        n = read(), m = read(), b = read();
        int big = 0;
        for (int i = 1; i <= n; i++) f[i] = read(), big = max(big, f[i]);
        while (m--) {
            int x, y, w;
            x = read(), y = read(), w = read();
            e[x].push_back({y, w});
            e[y].push_back({x, w});
        }
    
        int l = 0, r = big + 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (check(mid))
                r = mid;
            else
                l = mid + 1;
        }
        if (l != big + 1)
            printf("%d
    ", l);
        else
            puts("AFK");
    
        return 0;
    }
    P1462

     所以如果只想知道确定起点与终点间的最短路,Dijkstra可以这样剪枝一下,效果显著.

  • 相关阅读:
    安卓客户端获取手机号码
    安卓自定义控件之设计自己的提示dialog
    一步步打造自己的分页控件4
    C#winform小游戏之贪吃蛇重温C#
    android观察者模式
    Android开发之Java设计模式
    Android 用Animationlist实现逐帧动画
    图片压缩
    Android使用缓存优化ListView
    Android命令行启动程序正确使用技巧解析
  • 原文地址:https://www.cnblogs.com/Gaomez/p/14512615.html
Copyright © 2011-2022 走看看