2022summer-rating2-D 出行计划

链接:https://ac.nowcoder.com/acm/contest/38284/D
来源:牛客网

题目描述

\(wtz\) 所在的城市中有 \(n\) 个社区(用 \(1\sim n\) 表示)和 \(m\) 条连接两个社区的双向道路。\(wtz\) 计划拜访居住在这个城市的 \(k\) 个亲戚。给定 \(wtz\) 居住的社区、每个亲戚居住的社区以及每条道路的长度,请你为 \(wtz\) 规划一条从 \(wtz\) 居住的社区出发,经过每个亲戚的社区至少一次,最后回到 \(wtz\) 的社区的路线,使得路线的总长度最短。
输入描述:
第一行包含三个整数 \(n,m,k\) ( \(1\le n\le 10000, 0\le m\le 50000, 1\le k\le 16\))。

第二行包含一个整数,为 \(wtz\) 居住的社区。

第三行包含 \(k\) 个整数,为wtz的亲戚们的居住的社区。

接下来的 \(m\) 行中,每行包含三个整数 \(u,v,w\)($0\le w\le 10^9 $ ),表示一条连接社区 \(u\)\(v\) 、长度为 \(w\) 的双向道路。

可能存在两端是同一社区的道路。可能存在多条道路连接同一对社区。可能存在居住在相同社区的亲戚。可能存在与 \(wtz\) 居住在同一社区的亲戚。
输出描述:
路线的最短总长度。如果不存在满足要求的路线,输出-1。

思路:

通过 \(k + 1\)\(dij\) 算出 \(wtz\) 和亲戚两两之间距离的最小值,通过最小值求最短 \(Hamilton\) 回路 (状压dp)

代码:

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
const int N = 1e4 + 10, M = 1e5 + 110;
const ll inf = 1e15;
int head[N], ne[M], ver[M], edge[M], tot, n, m, k;
bool v[N];
ll d[N], w[17][17], f[1 << 17][17], a[17];
void add (int x, int y, int z) {
    ver[++tot] = y, edge[tot] = z, ne[tot] = head[x], head[x] = tot;
}
priority_queue<pair<ll, ll> > q;
void dijkstra(int s) {
    memset(v, 0, sizeof v);
    memset(d, 0x3f, sizeof d);
    d[a[s]] = 0, q.push(make_pair(0, a[s]));
    while (q.size()) {
        int x = q.top().second; q.pop();
        if (v[x]) continue;
        v[x] = 1;
        for (int i = head[x]; i; i = ne[i]) {
            int y = ver[i], z = edge[i];
            if (d[y] > d[x] + z) {
                d[y] = d[x] + z;
                q.push(make_pair(-d[y], y));
            }
        }
    }
 // for (int i = 0; i <= k; i++) cout << d[a[i]] << ' ';
 // cout << endl;
    for (int i = 0; i <= k; i++) w[s][i] = w[i][s] = min(w[s][i], d[a[i]]);
}
int main () {
    scanf("%d%d%d", &n, &m, &k);
    scanf("%lld", &a[0]);
    for (int i = 1; i <= k; i++) scanf("%lld", &a[i]);
    while (m--) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        if (x == y) continue;
        add(x, y, z), add(y, x, z);
    }
    memset(w, 0x3f, sizeof w);
    memset(f, 0x3f, sizeof f);
    for (int i = 0; i <= k; i++) dijkstra(i);
    f[1][0] = 0;
    for (int i = 1; i < (1 << (k + 1)); i++) {
        for (int j = 0; j < k + 1; j++) {
            if (i >> j & 1) {
                for (int l = 0; l <= k; l++) {
                    if ((i ^ 1 << j) >> l & 1) f[i][j] = min(f[i][j], f[i ^ (1 << j)][l] + w[l][j]);
                }
            }
        }
    }
    ll ans = inf;
    for (int i = 1; i <= k; i++) ans = min(ans, f[(1 << (k + 1)) - 1][i] + w[i][0]);
    if (ans >= inf) printf("-1\n");
    else printf("%lld\n", ans);
    return 0;
}

写的时候要注意代码细节,比如爆int之类的

posted @ 2022-08-10 22:47  misasteria  阅读(16)  评论(0编辑  收藏  举报