​ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层最短路)​

题目

给一个 n 点,m 条边的有向图,求 1 到 n 最短路,不过中间你可以选择任意的 k 边权值变为 0。

分析

题目关键在可以将 k 条边的权值变为 0。

ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层最短路)_权值 的思想,ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层最短路)_有向图_02

ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层最短路)_权值_03表示 1 号点到 i 点,走过 j 条免费的路的最短路值。

之前松弛操作为 ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层最短路)_最短路_04

现在变成: ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层最短路)_最短路_05

每次可以跟新状态可以选择使用或者不使用免费边。

最后结果就是在所有 k 情况下 ACM-ICPC 2018 南京赛区网络预赛 L. Magical Girl Haze(分层最短路)_有向图_06

注意范围 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;
}