1. 程式人生 > >【圖論-最短路】【P3393】逃離僵屍島

【圖論-最短路】【P3393】逃離僵屍島

hint eight pro urn ref wid rip tin #define

傳送門

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\n",ans);
    return 0;
}

Summary

  1、在不需要確切知道當前點和目標點的距離,而是需要知道每個點和給定的一些點的最近距離時,可以把給定的點全部壓到隊列裏面,一起spfa。

   2、spfa不能搜到就輸出(這不是廢話嗎……要不然直接bfs不就成了)

【圖論-最短路】【P3393】逃離僵屍島