云中谁寄锦书来?雁字回时,月满西楼。
Time Limit: 6000/3000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)Submit Status
在干完 TheBigOne(一票大的)之后,劫犯们得准备逃跑路线了。
城市可以看作 n 个点 m 条边的无向图,节点编号为 0 到 n−1,其中有 k 个点为安全屋,劫犯们只要到达其中一个安全屋就能摆脱警察的追捕。
劫犯们从联合储蓄(节点 0)出发,希望能在最短的时间内到达安全屋。
但是警察对劫犯紧追不舍,每当劫犯到达一个节点,警察就立刻封锁与该点相连的边。
由于警力有限,对于当前点警察最多能够封锁与其相连的 d 条边。
现在劫犯想知道,在最坏情况下,他们能到达安全屋的最短时间。
Input
第一行四个整数 n 、m 、k 和 d,含义如上文所描述。
接下来一行有 k 个整数,表示安全屋所在节点编号。
1≤n≤100000,1≤m≤1000000,0≤k≤n,0≤d≤m,0≤u 、v<n,0≤w≤10000 。
Output
若劫犯们不能到达安全屋,则输出 −1 。否则输出最坏情况下劫犯们到达安全屋的最短时间。
Sample input and output
Sample Input Sample Output
3 3 1 0 2
0 1 1
1 2 1
0 2 3
2
0 1 1
1 2 1
0 2 3
2
Source
2017 UESTC Training for Graph Theory
My Solution
题意:在n个点m条边的无向图上,有k个出口从起点出发,每到一个点(包括起点),
该点连出的边中有d条会被封锁,求最坏情况下到达出口的最短路。
Dijkstra拓展
由于求最坏情况下的最短路,对于每个点,显然最优的前d条边不能走。
对于边u->v,必然要先得到v到出口的最坏情况下的最短路才能得到u经过该边再到出口的最坏情况下的最短路,
也就是该边对于u的价值,所以要从出口往回考虑。
令f[i]表示i到出口的最坏情况下的最短路,同dijkstra算法一样,每个点i可以分为f[i]已确定的和f[i]未确定的
初始时自然是对于每个出口x,f[x]=0已确定。
对于f[v]已确定的点v,将边权为w的边u->v以f[v]+w为关键字加入小根堆中。
对于每个点i还要记录cnt[i]=k,表示到i后,i连出的最优的前k条边已被封锁。
每次取出堆顶对应的边u->v(若f[u]已确定直接弹出)则该边为u连出的(除已被封锁的边外)最优的边
若cnt[u]<d,该边必然会被封锁,那么将cnt[u]加1,弹出堆顶
若cnt[u]=d,那么可以确定f[u]=f[v]+w,再用u更新连向u的边,弹出堆顶。
重复这一过程直到确定f[0]的值,该值即为答案。
时间复杂度 O(nlogn)
空间复杂度 O(n)
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
typedef long long LL;
typedef pair<int, int> ii;
const int MAXN = 1e5 + 8;
const int MAXM = 1e6 + 8;
const int INF = 2e9 + 8;
vector<ii> sons[MAXN];
int dis[MAXN];
int cnt[MAXN];
bool vis[MAXN];
priority_queue<ii, vector<ii>, greater<ii> > pq;
//O(nlogn)
inline void ex_dijkstra(int n, int d)
{
memset(vis, false, sizeof vis);
int u, v, w, dist, sz, i;
while(!pq.empty()){
u = pq.top().second;
dist = pq.top().first;
pq.pop();
if(vis[u]) continue;
else{
if(cnt[u] == d){
dis[u] = dist;
vis[u] = true;
}
else{
cnt[u]++;
continue;
}
}
sz = sons[u].size();
for(i = 0; i < sz; i++){
v = sons[u][i].first;
w = sons[u][i].second;
if(dist + w < dis[v]){
//dis[v] = d + w;
pq.push(ii(dist + w, v));
}
}
}
}
int main()
{
#ifdef LOCAL
freopen("f.txt", "r", stdin);
//freopen("f.out", "w", stdout);
int T = 4;
while(T--){
#endif // LOCAL
ios::sync_with_stdio(false); cin.tie(0);
int n, m, k, d, i, u, v, w;
//cin >> n;
scanf("%d%d%d%d", &n, &m, &k, &d);
for(i = 0; i < m; i++){
scanf("%d%d%d", &u, &v, &w);
sons[u].push_back(ii(v, (int)w));
sons[v].push_back(ii(u, (int)w));
}
for(int i = 0; i <= n; i++){
dis[i] = INF;
}
for(i = 0; i < k; i++){
scanf("%d", &u);
dis[u] = 0;
cnt[u] = d;
pq.push(ii(0, u));
}
ex_dijkstra(n, d);
if(dis[0] == INF) puts("-1");
else printf("%d\n", dis[0]);
#ifdef LOCAL
while(!pq.empty()) pq.pop();
memset(cnt, 0, sizeof cnt);
for(i = 0; i <= n; i++) sons[i].clear();
cout << endl;
}
#endif // LOCAL
return 0;
}
Thank you!
------from ProLights