2020牛客多校第五场A-Portal

https://ac.nowcoder.com/acm/contest/5670/A

题意

有一个n个点m条边的带权图,你一开始在1号点,你要按顺序完成k个任务,第i个任务是先去a[i]再走
到b[i]。当你走到一个点上的时候,你可以在这个点创建一个传送门。当同时存在两个传送门的时候,
你可以在传送门之间不耗代价地传送。如果已经存在了两个传送门,你想再创建一个,就必须选择之前
的一个传送门关掉(关掉这个操作不耗时间,并且是远程操作,不需要走过去)。问完成所有任务的最
短总行走距离。

题解

我们考虑两个传送门的位置,其中一个传送门的位置其实是没有用的,我们随时可以在当前所在的位置开一个传送门,而不是走到一个传送门的位置再进行传送。

目标是要走完所有的\(2k\)个点,我们把任务拆开,设状态\(f[i][q]\)表示完成i个任务,这次的传送门设在q的最小花费,那么有以下情况

  1. 直接从c[i-1]走到c[i]
  2. 从c[i-1]走到q,在q开启传送门,枚举上一次传送门的位置p,从q传送到p,再从p走到c[i]
  3. 从c[i-1]走到q,在q开启传送门,直接从q走到c[i]
  4. 从c[i-1]传送到p,从p走到q,在q开启传送门,再从q走到c[i]

Floyd预处理出两点间的最短距离,直接转移即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct READ {
    inline char read() {
    #ifdef _WIN32
        return getchar();
    #endif
        static const int IN_LEN = 1 << 18 | 1;
        static char buf[IN_LEN], *s, *t;
        return (s == t) && (t = (s = buf) + fread(buf, 1, IN_LEN, stdin)), s == t ? -1 : *s++;
    }
    template <typename _Tp> inline READ & operator >> (_Tp&x) {
        static char c11, boo;
        for(c11 = read(),boo = 0; !isdigit(c11); c11 = read()) {
            if(c11 == -1) return *this;
            boo |= c11 == '-';
        }
        for(x = 0; isdigit(c11); c11 = read()) x = x * 10 + (c11 ^ '0');
        boo && (x = -x);
        return *this;
    }
} in;

const int N = 350;
ll G[N][N];
int a[N<<1];
ll f[N<<1][N];
const ll inf = 0x3f3f3f3f3f3f3f3f;
int main() {
    int n, m, k; in >> n >> m >> k;
    memset(G, inf, sizeof(G));
    for (int i = 1; i <= m; i++) {
        int u, v; ll w; in >> u >> v >> w;
        G[u][v] = min(G[u][v], w);
        G[v][u] = min(G[v][u], w);
    }
    for (int i = 1; i <= n; i++) G[i][i] = 0;
    k *= 2;
    for (int i = 1; i <= k; i++) in >> a[i];
    a[0] = 1;
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
            }
        }
    }
    memset(f, inf, sizeof(f));
    f[0][1] = 0;
    for (int i = 1; i <= k; i++) {
        for (int p = 1; p <= n; p++) {
            if (f[i-1][p] != inf) f[i][p] = min(f[i][p], f[i-1][p] + G[a[i-1]][a[i]]);
            for (int q = 1; q <= n; q++) {
                f[i][q] = min(f[i][q], f[i-1][p] + G[a[i-1]][q] + G[p][a[i]]);
                f[i][q] = min(f[i][q], f[i-1][p] + G[a[i-1]][q] + G[q][a[i]]);
                f[i][q] = min(f[i][q], f[i-1][p] + G[p][q] + G[q][a[i]]);
            }
        }
    }
    ll ans = inf;
    for (int i = 1; i <= n; i++) ans = min(ans, f[k][i]);
    printf("%lld\n", ans);
    return 0;
}
posted @ 2020-09-01 14:25  Artoriax  阅读(143)  评论(0编辑  收藏  举报