【图论-最短路】【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\n",ans); return 0; }
Summary
1、在不需要确切知道当前点和目标点的距离,而是需要知道每个点和给定的一些点的最近距离时,可以把给定的点全部压到队列里面,一起spfa。
2、spfa不能搜到就输出(这不是废话吗……要不然直接bfs不就成了)