分层图

分层图最短路

问题:

给定一个 \(n\) 个点 \(m\) 条边的有向图,有些边是特殊边,这些特殊边只能走 \(k\) 次,求路径最小值。

我们发现,这种题目,如果直接在图上跑,我们无法判断特殊边用了多少次,而且转移起来也比较麻烦。

因此,就引出了 分层图 的概念:

分析:

对于原路径,我们把所有路径类似于复制了 \(k-1\) 次,形成了 \(k\) 层。

  1. 对于非特殊边,直接在所有 同层\((x,y)\) 建立这样的边。
  2. 对于特殊边,我们从 \(i\) 层的 \(x\)\(i+1\) 层的 \(y\) 建立边。
  3. 对于每一层的 起点终点,我们需要将起点与起点,终点与终点依次连接起来,这样防止转移失败,注意建立的是 \(0\) 边。

最后,查询的就是 \(k\) 层的终点 所对应的值。

这是最简单的分层图类型,但是我们还会遇到很多类型的分层图题目,注意按照题目要求变化式子。

用法:

分层图主要用于 网络流,和 多类边的图

例题:

P4568 [JLOI2011]飞行路线

题意:

一共 \(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]回家的路

题解

posted @ 2021-11-02 21:11  Evitagen  阅读(417)  评论(0编辑  收藏  举报