P4568 飞行路线 分层图最短路
P4568 飞行路线 分层图最短路
分层图最短路
问题模型
求最短路时,可有\(k\)次更改边权(减为0)
思路
在普通求\(Dijkstra\)基础上,\(dis[x][j]\)多开一维\(j\)以存已用了多少次机会,然后每次松弛时,做完普通松弛操作后,还要使用一次机会(如果可以),类同\(DP\)。
每次普通松弛:
\[dis[to][j]=min\{dis[cur][j], dis[to][j]\}
\]
如果还可以使用(\(j<k\)):
\[dis[to][j+1] = min\{dis[cur][j], dis[to][j+1]\}
\]
AC Code:
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#define MAXN 10010
#define MAXK 11
#define MIN(A,B) ((A)<(B)?(A):(B))
using namespace std;
int n,m,k,s,e;
bool vis[MAXN][MAXK];
struct edge{
int v,w;
edge(int v, int w):v(v),w(w){}
};
vector <edge> mp[MAXN];
struct item{
int dis, k, v;
item(int dis, int k, int v):dis(dis), k(k), v(v){}
bool operator < (const item a) const{
return dis > a.dis;
}
};
int dis[MAXN][MAXK];
priority_queue <item> q;
void Dij(){
memset(dis, 0x3f, sizeof(dis));
dis[s][0]=0;
q.push(item(0,0,s));
while(!q.empty()){
item cur = q.top();q.pop();
if(vis[cur.v][cur.k]) continue;
vis[cur.v][cur.k] = 1;
for(register int i=0;i<mp[cur.v].size();++i){
int v = mp[cur.v][i].v, w = mp[cur.v][i].w;
if(cur.k<k&&!vis[v][cur.k+1]&&dis[v][cur.k+1]>dis[cur.v][cur.k]){ // 使用机会
dis[v][cur.k+1] = dis[cur.v][cur.k];
q.push(item(dis[v][cur.k+1], cur.k+1, v));
}
if(!vis[v][cur.k]&&dis[v][cur.k]>dis[cur.v][cur.k]+w){ // 普通松弛
dis[v][cur.k] = dis[cur.v][cur.k]+w;
q.push(item(dis[v][cur.k], cur.k, v));
}
}
}
}
int main()
{
scanf("%d %d %d %d %d", &n, &m, &k, &s, &e),s++,e++;
for(int i=1;i<=m;++i){
int a,b,c;
scanf("%d %d %d", &a, &b, &c),++a,++b;
mp[a].push_back(edge(b,c));
mp[b].push_back(edge(a,c));
}
Dij();
int ans=0x3ffffff;
for(int i=0;i<=k;++i)
ans = MIN(ans, dis[e][i]); // 遍历统计答案,机会不一定用完
printf("%d", ans);
return 0;
}