虫洞 (holes. cpp/c/pas)

题目描述

N 个虫洞, M 条单向跃迁路径. 从一个虫洞沿跃迁路径到另一个虫洞需要消耗一定量的燃料和 1 单位时间. 虫洞有白洞和黑洞之分. 设一条跃迁路径两端的虫洞质量差为 delta.

  1. 从白洞跃迁到黑洞, 消耗的燃料值减少 delta, 若该条路径消耗的燃料值变为负数的话, 取为 0.
  2. 从黑洞跃迁到白洞, 消耗的燃料值增加 delta.
  3. 路径两端均为黑洞或白洞, 消耗的燃料值不变化.

作为压轴题, 自然不会是如此简单的最短路问题, 所以每过 1 单位时间黑洞变为白洞, 白洞 变为黑洞. 在飞行过程中, 可以选择在一个虫洞停留 1 个单位时间, 如果当前为白洞, 则不 消耗燃料, 否则消耗 s [i] 的燃料. 现在请你求出从虫洞 1 到 N 最少的燃料消耗, 保证一定存在 1 到 N 的路线.

输入格式

第 1 行: 2 个正整数 N, M

第 2 行: N 个整数, 第 i 个为 0 表示虫洞 i 开始时为白洞, 1 表示黑洞.

第 3 行: N 个整数, 第 i 个数表示虫洞 i 的质量 w [i].

第 4 行: N 个整数, 第 i 个数表示在虫洞 i 停留消耗的燃料 s [i].

第 5...... M+4 行: 每行 3 个整数, u, v, k, 表示在没有影响的情况下, 从虫洞 u 到虫洞 v 需要消耗燃料 k.

输出格式

一个整数, 表示最少的燃料消耗.

样例输入

4 5
1 0 1 0
10 10 100 10
5 20 15 10
1 2 30
2 3 40
1 3 20
1 4 200
3 4 200

样例输出

130

数据范围

对于 30% 的数据:\(1 \leq N \leq 100, 1 \leq M \leq 500\)

对于 60% 的数据:\(1 \leq N \leq 1000,1 \leq M \leq 5000\)

对于 100% 的数据:\(1 \leq N \leq 5000,1 \leq M \leq 30000\)

\(1 \leq u, v \leq N, 1 \leq k, w[i], s[i] \leq 200\)

其中 20% 的数据为 \(1 \leq N \leq 3000\) 的链

样例说明

按照 1->3->4 的路线.

Explanation

将每一个节点拆成两个点, 即其在白洞的状态和在黑洞的状态, 各一个.

然后考虑时间的变化后分类讨论, 在原来的两个点拆出来的四个点之间建边 (具体参见源码 的 addedge 函数等等).

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <queue>

using namespace std;
typedef long long lli;
const int maxn = 10010, maxm = 150100;
const lli infinit = 0x007f7f7f7f7f7f7fll;

class SPFA
{
public:
    struct edge {
        int u, v;
        lli len;
        edge *next;
    } *edges[maxn], epool[maxm];
    int n, s, cnt;
    lli dist[maxn];
    bool inque[maxn];
    void addedge(int u, int v, lli len)
    {
        // printf("addedge %d -> %d : %lld\n", u, v, len);
        edge *p = &epool[++cnt];
        p->u = u; p->v = v; p->len = len;
        p->next = edges[u]; edges[u] = p;
        return ;
    }
    void eval(void)
    {
        for (int i = 1; i <= n; i++)
            dist[i] = infinit,
            inque[i] = false;
        queue<int> que;
        que.push(s);
        dist[s] = 0, inque[s] = true;
        while (!que.empty()) {
            int p = que.front();
            que.pop();
            for (edge *ep = edges[p]; ep; ep = ep->next)
                if (dist[p] + ep->len < dist[ep->v]) {
                    dist[ep->v] = dist[p] + ep->len;
                    if (!inque[ep->v]) {
                        que.push(ep->v);
                        inque[ep->v] = true;
                    }
                }
            inque[p] = false;
            continue;
        }
        // Results must be retrieved manually by user from dist[...].
        return ;
    }
} graph;

int n, m;
bool I[maxn]; // Initial state (white: false, black: true)
lli W[maxn]; // Mass of the wormhole
lli S[maxn]; // The cost of staying at this wormhole

int main(int argc, char** argv)
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &I[i]);
    for (int i = 1; i <= n; i++) scanf("%lld", &W[i]);
    for (int i = 1; i <= n; i++) scanf("%lld", &S[i]);
    // Here comes the addedge... which is a difficult thing to do
    // Graph is oriented in such a way:
    //   2 * i - 1 -> wormhole #i @ state white hole
    //   2 * i     -> wormhole #i @ state black hole
    graph.n = n * 2;
    graph.s = I[1] ? 2 : 1;
    // The following procedures determine how much cost would be dealt when
    // warping between wormholes.
    for (int i = 1, u, v; i <= m; i++) {
        lli k;
        scanf("%d%d%lld", &u, &v, &k);
        // If both are of the same type... otherwise...
        if (I[u] == I[v]) {
            // Warping from white to what would be a white hole
            graph.addedge(2 * u - 1, 2 * v, k);
            // Warping from black to what would be a black hole
            graph.addedge(2 * u, 2 * v - 1, k);
        } else {
            // Warping from white hole to what would be a black hole
            graph.addedge(2 * u - 1, 2 * v - 1, max(0ll, k - abs(W[u] - W[v])));
            // Warping from black hole to what would be a white hole
            graph.addedge(2 * u, 2 * v, k + abs(W[u] - W[v]));
        }
    }
    // The following procedures determine how much cost would be dealt when
    // staying at the same wormhole.
    for (int i = 1; i <= n; i++) {
        // At time this is a white hole
        graph.addedge(2 * i - 1, 2 * i, 0);
        // At time this is a black hole
        graph.addedge(2 * i, 2 * i - 1, S[i]);
    }
    // Execute shortest path with SPFA.
    graph.eval();
    // for (int i = 1; i <= graph.n; i++)
    //     printf("#%d -> %lld;\n", i, graph.dist[i]);
    lli res = min(
        graph.dist[2 * n - 1],
        graph.dist[2 * n]);
    printf("%lld\n", res);
    return 0;
}