洛谷 P3393 逃离僵尸岛

题目描述

小 a 住的国家被僵尸侵略了!小 a 打算逃离到该国唯一的国际空港逃出这个国家。

该国有 \(N\) 个城市,城市之间有道路相连。一共有 \(M\) 条双向道路。保证没有自环和重边。

其中 \(K\) 个城市已经被僵尸控制了,如果贸然闯入就会被感染 TAT...所以不能进入。由其中任意城市经过不超过 \(S\) 条道路就可以到达的别的城市,就是危险城市。换句话说只要某个城市到任意被僵尸控制的城市距离不超过 \(S\),就是危险的。

小 a 住在 \(1\) 号城市,国际空港在 \(N\) 号城市,这两座城市没有被侵略。小a走每一段道路(从一个城市直接到达另外一个城市)得花一整个白天,所以晚上要住旅店。安全的的城市旅馆比较便宜要 \(P\) 元,而被侵略的城市,旅馆要进行安保措施,所以会变贵,为 \(Q\) 元。所有危险的城市的住宿价格一样,安全的城市也是。在 \(1\) 号城市和 \(N\) 城市,不需要住店。

小 a 比较抠门,所以他希望知道从 \(1\) 号城市到 \(N\) 号城市所需要的最小花费。

输入数据保证存在路径,可以成功逃离。

输入格式

第一行 4 个整数 \(N,M,K,S\)。

第二行两个整数 \(P,Q\)。

接下来 \(K\) 行,每行一个整数 \(c_i\),表示僵尸侵占的城市编号。

接下来 \(M\) 行,\(a_i,b_i\),表示一条无向边。

输出格式

一个整数表示最低花费。

输入输出样例 #1

输入 #1

13 21 1 1
1000 6000
7
1 2
3 7
2 4
5 8
8 9
2 5
3 4
4 7
9 10
10 11
5 9
7 12
3 6
4 5
1 3
11 12
6 7
8 11
6 13
7 8
12 13

输出 #1

11000

说明/提示

对于 \(20\%\) 数据,\(N\le 50\)。

对于 \(100\%\) 数据,\(2\le N\le 10^5\),\(1\le M\le 2\times 10^5\),\(0\le K\le N - 2\),\(0\le S\le 10^5\),\(1\le P< Q\le 10^5\)。

算法分析

这道题还是挺裸的,就是先求出哪些是危险的城市,再跑一遍\(dijkstra\),求出\(1\)到\(n\)的最短距离,输出即可。但是输入没有给边权,那我们可以自己造边权,很容易想到把两端的点权加起来,然后减掉\(1\)和\(n\)的点权再除以二就行了。

思路整理

  1. 先对输入的数进行处理,标记僵尸城市,然后记录边。
  2. 接着跑一边\(bfs\),处理出危险城市,标记。
  3. 然后建边,造边权。
  4. 跑一边\(dijkstra\)。
  5. 最后输出\(1\)到\(n\)的最短距离减\(1\)的点权和\(n\)的点权,除以\(2\)就行了。

AC代码

#include<bits/stdc++.h>
#define int long long
#define mp make_pair
using namespace std;
struct node{
    int val;
    int to,nxt;
};
struct node2{
    int x;
    int cnt;
};
int n,m,k,s,p1,p2;
int cnt = 0;
int a[1000005];
int b[1000005];
node c[1000005];
int head[4000005];
bool vis[1000005];
vector<pair<int,int>> d[1000005];
queue<node2> q;
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> pq;
int get(int x){
    if(a[x]==0){
        return p1;
    }else{
        return p2;
    }
}
void bfs(){
    while(!q.empty()){
        node2 pos = q.front();
        q.pop();
        if(pos.cnt>=s){
            continue;
        }
        for(auto i : d[pos.x]){
            if(a[i.first]==0){
                a[i.first] = 1;
                q.push(node2{i.first,pos.cnt+1});
            }
        }
    }
}
void dijkstra(){
    for(int i = 1; i<=n; i++){
        b[i] = 1e18;
    }
    b[1] = 0LL;
    pq.push(mp(0LL,1));
    while(!pq.empty()){
        int pos = pq.top().second;
        pq.pop();
        if(vis[pos]==1){
            continue;
        }
        vis[pos] = 1;
        for(int i = head[pos]; i; i = c[i].nxt){
            if(a[c[i].to]==-1){
                continue;
            }
            if(b[pos]+c[i].val<b[c[i].to]){
                b[c[i].to] = b[pos]+c[i].val;
                pq.push(mp(b[c[i].to],c[i].to));
            }
        }
    }
}
signed main(){
    cin>>n>>m>>k>>s;
    cin>>p1>>p2;
    for(int i = 1; i<=k; i++){
        int x;
        cin>>x;
        a[x] = -1;
        q.push(node2{x,0});
    }
    for(int i = 1; i<=m; i++){
        int x,y;
        cin>>x>>y;
        cnt++;
        c[cnt] = node{0,y,head[x]};
        head[x] = cnt;
        d[x].push_back(mp(y,cnt));
        cnt++;
        c[cnt] = node{0,x,head[y]};
        head[y] = cnt;
        d[y].push_back(mp(x,cnt));
    }
    bfs();
    for(int i = 1; i<=n; i++){
        for(auto j : d[i]){
            int sum = 0;
            if(a[i]==0){
                sum+=p1;
            }else{
                sum+=p2;
            }
            if(a[j.first]==0){
                sum+=p1;
            }else{
                sum+=p2;
            }
            c[j.second].val = sum;
        }
    }
    dijkstra();
    cout<<(b[n]-get(1)-get(n))/2;
    return 0;
}

总结

因为作者懒,所以没打注释,而且代码冗长,其实这题代码不好写,但思路很好想,所以比较简单,算裸的最短路题。

posted @ 2026-08-31 16:58  xmgene  阅读(10)  评论(0)    收藏  举报