GYM 101933D(最短路、二分、dp)

要点

  • 非要先来后到暗示多源最短路,求最小的最大值暗示二分
  • 二分内部的check是关键,dp处理一下,\(dp[i]\)表示第\(i\)笔订单最早何时送达,如果在ddl之前到不了则\(return\ 0\)。我觉得其中\(time\)变量的维护很好地使复杂度降了一维。
  • 第一发WA点:算法看了一遍感觉没有可改的,就把二分的\(r\)调大了,又把\(longlong\)的输入输出改为流,莽试一发就过了……
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;

typedef long long ll;
const int maxn = 1005, maxm = 5005;
const ll INF = 1e18;

int n, m, k;
ll s[maxn], u[maxn], t[maxn];
struct Edge {
    int to, nxt, cost;
}e[maxm << 1];
int head[maxn], tot;
ll dis[maxn][maxn];

void add(int u, int v, int c) {
    e[++tot].to = v, e[tot].cost = c, e[tot].nxt = head[u], head[u] = tot;
}

void dij(int st) {
    for (int i = 1; i <= n; i++)    dis[st][i] = INF;
    dis[st][st] = 0;
    
    typedef pair<ll, int> pli;
    priority_queue<pli, vector<pli>, greater<pli>> Q;
    Q.push({0, st});

    while (Q.size()) {
        ll d = Q.top().first;
        int p = Q.top().second;
        Q.pop();
        if (d > dis[st][p]) continue;
        for (int i = head[p]; i; i = e[i].nxt) {
            int t = e[i].to;
            if (dis[st][t] > d + e[i].cost) {
                dis[st][t] = d + e[i].cost;
                Q.push({dis[st][t], t});
            }
        }
    }
}

bool ok(ll D) {
    ll dp[maxn];
    dp[0] = 0;
    for (int i = 1; i <= k; i++)
        dp[i] = INF;
    for (int i = 1; i <= k; i++) {
        ll val = dis[1][u[i]];
        ll st = max(t[i], dp[i - 1] + dis[u[i - 1]][1]);
        dp[i] = min(dp[i], st + val);
        if (dp[i] > s[i] + D)   return 0;

        ll time = s[i] + D - st - val;
        if (time < 0)   continue;
        for (int j = i + 1; j <= k; j++) {
            val += dis[u[j - 1]][u[j]];
            if (t[j] > st) {
                time -= t[j] - st;
                st = t[j];
            }
            time = min(time, s[j] + D - st - val);
            if (time < 0)   break;
            dp[j] = min(dp[j], st + val);
        }
    }
    return 1;
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1, u, v, c; i <= m; i++) {
        scanf("%d %d %d", &u, &v, &c);
        add(u, v, c), add(v, u, c);
    }
    scanf("%d", &k);
    for (int i = 1; i <= k; i++)
        cin >> s[i] >> u[i] >> t[i];

    for (int i = 1; i <= n; i++) {
        dij(i);
    }
    ll l = 0, r = 1e18, ans;
    while (l <= r) {
        ll mid = (l + r) >> 1;
        if (ok(mid))    r = mid - 1, ans = mid;
        else    l = mid + 1;
    }
    cout << ans << '\n';
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/AlphaWA/p/10867128.html