【图论-最短路】【P3393】逃离僵尸岛

传送门

Description

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

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

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

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

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

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

Input

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

第二行2个整数(P,Q)

接下来K行,ci,表示僵尸侵占的城市

接下来M行,ai,bi,表示一条无向边

Output

一个整数表示最低花费

Sample Input

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

Sample Output

11000

Hint

对于100%数据,2 ≦ N ≦ 100000, 1 ≦ M ≦ 200000, 0 ≦ K ≦ N - 2, 0 ≦ S ≦ 100000

1 ≦ P < Q ≦ 100000

Solution

  思路十分清晰:先求出所有是危险区域的点,然后跑一遍spfa。

  以下为了叙述方便,记被占领的点为黑色的点,危险的点为灰色的点,安全的点为白色的点。

  但是怎么求灰色的点呢?暴力的方法当然是直接枚举所有黑色的点,各跑一遍spfa。然后存下来,但是一共最多有n(同阶)个点,spfa最多要n(同阶)层……然后你就炸了

  考虑我们在这一遍spfa的时候并不需要清楚的知道它离我们规定的起点的距离。只需要知道它离距离他最近的点的距离即可。所以我们可以一次性把所有的黑色点压到队列里面一起spfa。这样我们的frog数组确切的含义就是距离他最近的黑色点离他的距离。

  然后我们使用数组记录那些点是灰色的,然后跑一边真正的spfa。

  几个坑点:1、long long

       2、黑色点不能走

       3、最后一个点和第一个点不需要算点权。

       4、由于我们每条路走过去都可能由两个花费,所以不能在spfa第一次搜到目标点的时候直接输出exit(0).(这点好像很基础但我太久没写忘了)

Code

#include<queue>
#include<cstdio>
#include<cstring>
#define int long long
#define maxn 100010
#define maxm 400010

inline void qr(int &x) {
    char ch=getchar();int f=1;
    while(ch>'9'||ch<'0')    {
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')    x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x*=f;
    return;
}

inline int max(const int &a,const int &b) {if(a>b) return a;else return b;}
inline int min(const int &a,const int &b) {if(a<b) return a;else return b;}
inline int abs(const int &x) {if(x>0) return x;else return -x;}

inline void swap(int &a,int &b) {
    int c=a;a=b;b=c;return;
}

int n,m,k,s,p,q;

const long long INF = (1ll<<62);

struct Edge {
    int to,nxt;
};
Edge edge[maxm];int hd[maxn],ecnt;
inline void cont(const int &from,const int&to) {
    edge[++ecnt].to=to;
    edge[ecnt].nxt=hd[from];
    hd[from]=ecnt;
}

int a,b,c[maxn],frog[maxn],ans=1926081700000;
bool dangerous[maxn],cant[maxn];
std::queue<int>Q;

void jiadespfa() {                            //拼音大法好
    for(int i=1;i<=n;++i)    frog[i]=INF;
    for(int i=1;i<=k;++i)    {frog[c[i]]=0;Q.push(c[i]);cant[c[i]]=true;}
    while(!Q.empty()) {
        int h=Q.front();Q.pop();
        for(int i=hd[h];i;i=edge[i].nxt) {
            int &to=edge[i].to;
            if(frog[to]>frog[h]+1) {
                frog[to]=frog[h]+1;if(frog[to]<=s) {
                    dangerous[to]=true;Q.push(to);
                }
            }
        }
    }
}

void zhendespfa() {                            //拼音大法好
    for(int i=1;i<=n;++i)    frog[i]=INF;
    frog[1]=0;Q.push(1);
    while(!Q.empty()) {
        int h=Q.front();Q.pop();
        for(int i=hd[h];i;i=edge[i].nxt) {
            int &to=edge[i].to;int v=(dangerous[to]?q:p);
            if(cant[to])    continue;
            if(to==n) {
                ans=min(ans,frog[h]);
                continue;
            }
            if(frog[to]>frog[h]+v) {
                frog[to]=frog[h]+v;Q.push(to);
            }
        }
    }
}

main() {
    qr(n);qr(m);qr(k);qr(s);qr(p);qr(q);
    for(int i=1;i<=k;++i)    qr(c[i]);
    for(int i=1;i<=m;++i) {
        a=b=0;qr(a);qr(b);cont(a,b);cont(b,a);
    }
    jiadespfa();
    zhendespfa();
    printf("%lld
",ans);
    return 0;
}

Summary

  1、在不需要确切知道当前点和目标点的距离,而是需要知道每个点和给定的一些点的最近距离时,可以把给定的点全部压到队列里面,一起spfa。

   2、spfa不能搜到就输出(这不是废话吗……要不然直接bfs不就成了)

原文地址:https://www.cnblogs.com/yifusuyi/p/9245401.html