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之类的
标签:rating2,出行,社区,2022summer,int,wtz,居住,亲戚,le 来源: https://www.cnblogs.com/misasteria/p/16574216.html