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\n", l);
    else
        puts("AFK");

    return 0;
}
P1462

 


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

posted @ 2021-03-10 16:41  goverclock  阅读(57)  评论(0编辑  收藏  举报