分层图
分层图最短路
问题:
给定一个 \(n\) 个点 \(m\) 条边的有向图,有些边是特殊边,这些特殊边只能走 \(k\) 次,求路径最小值。
我们发现,这种题目,如果直接在图上跑,我们无法判断特殊边用了多少次,而且转移起来也比较麻烦。
因此,就引出了 分层图 的概念:
分析:
对于原路径,我们把所有路径类似于复制了 \(k-1\) 次,形成了 \(k\) 层。
- 对于非特殊边,直接在所有 同层 的 \((x,y)\) 建立这样的边。
- 对于特殊边,我们从 \(i\) 层的 \(x\) 到 \(i+1\) 层的 \(y\) 建立边。
- 对于每一层的 起点 和 终点,我们需要将起点与起点,终点与终点依次连接起来,这样防止转移失败,注意建立的是 \(0\) 边。
最后,查询的就是 第 \(k\) 层的终点 所对应的值。
这是最简单的分层图类型,但是我们还会遇到很多类型的分层图题目,注意按照题目要求变化式子。
用法:
分层图主要用于 网络流,和 多类边的图。
例题:
题意:
一共 \(n\) 个点,\(m\) 条边,可以选择 \(k\) 条边使边权变为 \(0\) ,求从起点到终点的最短路。
分析:
一眼板子题,直接将每条航线建立时,从 \(x\) 向下面一层的 \(y\) 建立一条 \(0\) 边即可。
#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define mk make_pair
using namespace std;
const int N=3e6+5;
int n,m,k,s,t;
int ans;
int nxt[N],ver[N],tot,edge[N],head[N];
int dis[N],vis[N];
void add(int x,int y,int z){
ver[++tot]=y; edge[tot]=z; nxt[tot]=head[x]; head[x]=tot;
}
priority_queue<pii > q;
void dijkstra(int s){
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[s]=0;
q.push(mk(0,s));
while(!q.empty()){
int x=q.top().second; q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i],z=edge[i];
if(dis[y]>dis[x]+z){
dis[y]=dis[x]+z;
q.push(mk(-dis[y],y));
}
}
}
}
signed main(){
cin>>n>>m>>k>>s>>t;
for(int i=1,x,y,z;i<=m;i++){
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z); add(y,x,z);
for(int j=1;j<=k;j++){
add((j-1)*n+x,j*n+y,0); add((j-1)*n+y,j*n+x,0);
add(j*n+x,j*n+y,z); add(j*n+y,j*n+x,z);
}
}
for(int i=1;i<=k;i++) add(t+(i-1)*n,t+i*n,0),add(s+(i-1)*n,s+i*n,0);
dijkstra(s);
cout<<dis[n*k+t]<<endl;
system("pause");
return 0;
}
[SHOI2012]回家的路
不关注的有难了😠😠😠https://b23.tv/hoXKV9