[USACO23FEB] Moo Route II S 题解

思路

首先,这道题目是一个比较明显的图论最短路,但是由于时间可以从后往前,想直接跑最短路而不用玄学方法的话——迪杰斯特拉怕只有怔怔地望着的份了。当然,是否有玄学方法我就不得而知了。

SPFA 的 LLL 优化

“燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。”聪明的,你告诉我,SPFA 死了就没有再活的时候吗?

非然也。

我们直接按照题目的边作为图上的边,跑 SPFA 算法。注意一开始我们对一个点连出去的所有边排序,那么这样对于 SPFA 中枚举边的时候发现边被分为两类,刚好是两段——一段是起始时间太早导致无法乘坐的,位于其后面的一段是能去拓展其它点的边。

那么我们每次维护分隔点的位置即可。

“我掩面叹息,但是新来的日子的影儿又开始在叹息里闪过了”

我掩面叹息,但是强大的 SPFA 早已在叹息里跑过了。

此做法的代码是通过了,但是我就不给了。

记忆化 SPFA

这只能应对有特殊性质的题目。

这个做法时间复杂度有保证,代码又异常短。

首先还是对边排序,还是跑 SPFA 最短路。改动的只是我们把每次拓展出去过的边都删掉。毕竟每条边只可能在第一次遍历到时更新其它点,从第二次开始必定不会更新成功。

#include <bits/stdc++.h>
#define L(i, a, b) for(register int i = a; i <= b; i++)
#define R(i, a, b) for(register int i = a; i >= b; i--)
using namespace std;
const int N = 2e5 + 10;
struct Edge{
    int v, s, t;
    friend bool operator < (Edge a, Edge b){
        return a.s == b.s? a.v < b.v : a.s < b.s;
    }
};
int n, m, a[N], dis[N], vis[N], id[N];
queue<int> q; vector<Edge> g[N];
void Spfa(){
    L(i, 2, n) dis[i] = 2e9;
    vis[1] = 1, q.push(1);
    while(!q.empty()){
        int u = q.front(); q.pop();
        int tm = (u == 1? 0 : dis[u] + a[u]);
        while(id[u] && g[u][id[u] - 1].s >= tm) id[u]--;
        vis[u] = 0;
        L(i, id[u], (int)g[u].size() - 1){
            int v = g[u][i].v, t = g[u][i].t;
            if(t < dis[v]){
                dis[v] = t;
                if(!vis[v]) vis[v] = 1, q.push(v);
            }
        }
        while(g[u].size() > id[u]) g[u].pop_back(); id[u] = g[u].size();
    }
}
int main(){
    scanf("%d%d", &n, &m);
    L(i, 1, m){
        int u, v, s, t;
        scanf("%d%d%d%d", &u, &s, &v, &t);
        g[u].push_back({v, s, t});
    }
    L(i, 1, n){
        scanf("%d", &a[i]);
        sort(g[i].begin(), g[i].end()), id[i] = g[i].size();
    }
    Spfa();
    L(i, 1, n){
        if(dis[i] == 2e9) printf("-1\n");
        else printf("%d\n", dis[i]);
    }
    return 0;
}
posted @ 2023-03-08 11:38  徐子洋  阅读(79)  评论(0)    收藏  举报  来源