ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层最短路)
题目
给一个 n 点,m 条边的有向图,求 1 到 n 最短路,不过中间你可以选择任意的 k 边权值变为 0。
分析
题目关键在可以将 k 条边的权值变为 0。
用 D P DP DP 的思想, d i s dis dis 数组变为二维的。
d i s [ i ] [ j ] dis[i][j] dis[i][j]表示 1 号点到 i 点,走过 j 条免费的路的最短路值。
之前松弛操作为 d i s [ v ] = m i n ( d i s [ v ] , d i s [ u ] + a [ u ] [ v ] ) dis[v] = min(dis[v], dis[u] + a[u][v]) dis[v]=min(dis[v],dis[u]+a[u][v])。
现在变成: d i s [ v ] [ j ] = m i n ( d i s [ v ] [ j ] , d i s [ u ] [ j ] + a [ u ] [ v ] , d i s [ x ] [ j − 1 ] ) dis[v][j] = min(dis[v][j], dis[u][j] + a[u][v], dis[x][j - 1]) dis[v][j]=min(dis[v][j],dis[u][j]+a[u][v],dis[x][j−1])
每次可以跟新状态可以选择使用或者不使用免费边。
最后结果就是在所有 k 情况下 d i s [ n ] dis[n] dis[n] 最小的值。
注意范围 2e5。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
int _, n, m, k, x, y;
int to[N], ne[N], h[N], idx;
ll dis[N][15], w[N], val; // dis[i][k] 走到 i ,经过了 k 条免费的路
int vis[N][15];
void add(int a, int b, ll val){
to[idx] = b, w[idx] = val, ne[idx] = h[a], h[a] = idx++;
}
struct node{
int u, kk;
ll di;
friend bool operator < (const node& a, const node& b) {
return a.di > b.di;
}
};
void dijk(){
memset(dis, INF, sizeof(dis));
memset(vis, 0, sizeof(vis));
priority_queue<node> q; // 距离小的优先
dis[1][0] = 0; // 初始化
q.push(node{1, 0, 0});
while(!q.empty()){
node cnt = q.top();
q.pop();
if(vis[cnt.u][cnt.kk])
continue;
vis[cnt.u][cnt.kk] = 1;
for(int i = h[cnt.u]; ~i; i = ne[i]){ // 枚举当前节点所相连的边
int x = to[i]; // 不消耗乘车权
if(dis[x][cnt.kk] > cnt.di + w[i]){ // 松弛操作
dis[x][cnt.kk] = cnt.di + w[i];
q.push(node{x, cnt.kk, dis[x][cnt.kk]});
}
if(cnt.kk + 1 <= k){ // 消耗乘车权
if(dis[x][cnt.kk+1] > cnt.di){
dis[x][cnt.kk + 1] = cnt.di;
q.push(node{x, cnt.kk + 1, dis[x][cnt.kk + 1]});
}
}
}
}
ll ans = INF;
for(int i = 0; i <= k; i++){
ans = min(dis[n][i], ans);
}
printf("%lld\n", ans);
}
int main(){
for (scanf("%d", &_); _; _--)
{
scanf("%d%d%d", &n, &m, &k);
memset(h, -1, sizeof(h)), idx = 0;
while(m--){
scanf("%d%d%lld", &x, &y, &val);
add(x, y, val);
}
dijk();
}
return 0;
}