分层图最短路[学习笔记]

分层图最短路,主要是用来求解允许少量次数修改边权的最短路问题,由于修改此数较小,所以解法也有些许暴力.下面,让我们来看这么一道例题:

description:

有一\(n\)\(m\)边构成的连通图,给定起点\(S\)和终点\(T\),现允许你将至多\(k(k\leq10)\)条边的边权修改为\(0\),请你求出修改次数不超过\(k\)的情况下,\(S\)\(T\)的最短路径长度.

solution:

此题是分层图最短路模板题.首先不难想到一一枚举\(k\)条免费边,然后跑最短路,但算法复杂度\(O((m+n)logn \cdot C_{m}^{k})\),极度暴力,连最小数据范围都十分吃力,于是我们便需引入新的思路.

由于\(k\)较小,我们不妨构造\(k+1\)个相同的联通图,其中第\(i\)\(i+1\)个连通图中对应的边我们连上权值为\(0\)的单向边,以表示免费的边.用此方式,每次只能跑向层数更高的图,或在本层图内跑.且每跑进一个新图,意味着我们跑过了一条免费边,所以构造该分层图可以完美求解本题.答案即为每层终点的最短路的最小值,为方便起见,我们可以在每层终点向下一层终点连一条权值为\(0\)的单向边以传递最短路,最后输出最后一层终点的答案即可.

code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define R register
#define next kdjadskfj
#define debug puts("mlg")
#define mod 1004535809
#define Mod(x) ((x%mod+mod)%mod)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
inline ll read();
inline void write(ll x);
inline void writeln(ll x);
inline void writesp(ll x);
ll n,m,k;
ll head[3000000],next[3000000],to[3000000],w[3000000],tot;
ll S,T;
ll d[3000000];
bool vis[3000000];

inline void add(ll x,ll y,ll z){to[++tot]=y;next[tot]=head[x];head[x]=tot;w[tot]=z;}

//queue<ll>q;
//inline void spfa(){
//	memset(d,0x3f,sizeof d);
//	q.push(S);d[S]=0;
//	while(q.size()){
//		ll x=q.front();q.pop();vis[x]=false;
//		for(R ll i=head[x],ver;i;i=next[i]){
//			ver=to[i];
//			if(d[ver]>d[x]+w[i]){
//				d[ver]=d[x]+w[i];
//				if(!vis[ver]){
//					vis[ver]=true;
//					q.push(ver);
//				}
//			}
//		}
//	}
//}

ll ans;
priority_queue<pair<ll,ll> >q;
inline void dijkstra(){
	memset(d,0x3f,sizeof d);
	d[S]=0;;
	q.push(make_pair(0,S));
	while(q.size()){
		ll x=q.top().second;q.pop();
		if(vis[x]) continue;
		vis[x]=true;
		for(R ll i=head[x],ver;i;i=next[i]){
			ver=to[i];
			if(d[ver]>d[x]+w[i]){
				d[ver]=d[x]+w[i];
				q.push(make_pair(-d[ver],ver));
			}
		}
	}
}

int main(){	
	n=read();m=read();k=read();
	S=read()+1;T=read()+1;
	while(m--){
		ll x=read()+1,y=read()+1,z=read();
		add(x,y,z);add(y,x,z);
		for(R ll i=1;i<=k;i++){
			add(x+i*n,y+i*n,z);add(y+i*n,x+i*n,z);
			add(x+(i-1)*n,(y+i*n),0);
			add(y+(i-1)*n,(x+i*n),0);
		}
	}
	for(R ll i=1;i<=k;i++){
		add(T+(i-1)*n,T+i*n,0);
	}
//	spfa();
	dijkstra();
	writeln(d[T+n*k]);
}
posted @ 2020-07-31 15:24  月落乌啼算钱  阅读(105)  评论(0编辑  收藏  举报