题解【逃离僵尸岛】(洛谷P3393)
题意
有\(n\)个点\(m\)条边。现有一些点被感染,其余的点中与被感染的点距离\(\leq S\) 的点是危险的点,点权为\(Q\);与所有被感染的点距离\(>S\)的点为安全的点,点权为\(P\)。\(1\)号店与\(n\)号点点权始终为\(0\),求\(1\)号点走到\(n\)号点的最小全值(被感染的点不能走)。
分析一下
- 最短路板子题吖~
(调了一上午) - 首先通过\(bfs\)将所有的危险点求出来,并把点赋上权值。
- 然后跑最短路就好了。
- 点权怎么跑最短路?
- 将边权当做连接的两个点的点权之和,输出答案时除以二就好了。
代码君
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define int long long
using namespace std;
const int inf = 1e9 + 7;
const int MAXN = 100005;
int n, m, K, S, P, Q;
int val[MAXN], dis[MAXN];
int dan[MAXN], dep[MAXN], vis[MAXN], zomb[MAXN];
int edgenum, front[MAXN], vet[MAXN << 2], nxt[MAXN << 2];
queue<int> que;
inline void addedge(int u, int v) {
nxt[++edgenum] = front[u];
front[u] = edgenum;
vet[edgenum] = v;
}
void bfs() {
for (int i = 1; i <= K; i++) {
dan[zomb[i]] = 1;
dep[zomb[i]] = 0;
que.push(zomb[i]);
}
while (!que.empty()) {
int u = que.front(); que.pop();
if (dep[u] == S)
continue;
for (int e = front[u]; e; e = nxt[e]) {
int v = vet[e];
if (dan[v])
continue;
dan[v] = 1;
dep[v] = dep[u] + 1;
que.push(v);
}
}
} //bfs求出所有危险点。
void SPFA() {
for (int i = 1; i <= n; i++)
dis[i] = inf * 9999, vis[i] = 0;
dis[1] = 0, vis[1] = 1;
que.push(1);
while (!que.empty()) {
int u = que.front(); que.pop();
vis[u] = 0;
for (int e = front[u]; e; e = nxt[e]) {
int v = vet[e];
if (dis[v] > dis[u] + val[u] + val[v]) { //将边权当做两个点权之和。
dis[v] = dis[u] + val[u] + val[v];
if (!vis[v]) {
vis[v] = 1;
que.push(v);
}
}
}
}
} //最短路
signed main() {
scanf("%lld%lld%lld%lld%lld%lld", &n, &m, &K, &S, &P, &Q);
for (int i = 1; i <= K; i++)
scanf("%lld", &zomb[i]);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%lld%lld", &x, &y);
addedge(x, y), addedge(y, x);
}
bfs();
for (int i = 1; i <= n; i++)
if (dan[i])
val[i] = Q;
else val[i] = P; //赋点权
for (int i = 1; i <= K; i++)
val[zomb[i]] = inf; //这样就能在跑最短路时不取被感染的点
val[1] = val[n] = 0;
SPFA();
printf("%lld\n", dis[n] / 2);
return 0;
}