【圖論-最短路】【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 613 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】逃離僵屍島