分层图的妙用

介绍

分层图是一种很容易理解并且用途广泛的图,多用于求最短路,它其实是普通图最短路的加强版,可以解决一些图论题目的特殊操作,这个特殊操作就是我们去分的“层”。

思想

做分层图的题,只要把题目读懂,明白到底“层”分在哪就能解题,比如说:

给定一张\(n\)个点\(m\)条边的图,给出原点\(s\)和目标点\(t\),可以创造\(k\)次虫洞(不一定要用完),使得任意\(i\)\(j\)的距离变为\(0\),请求出\(s\)\(t\)的最短距离

那么,这道题有区别与普通最短路的地方明显就在于可以创造虫洞,而我们需要分的层就体现在创造虫洞的次数,下面我们就用图演示分层算法,假设此时原点为1,目标点为5,可以创造2次虫洞:
在这里插入图片描述
首先我们先将图复制为3份 为什么不是2份?因为一个虫洞也不创建也算一种情况
在这里插入图片描述
然后,对于某个点来说(这里以点4为例),它既有连接本层内的原始边(蓝色),而且我们还可以创造一次虫洞,使得它直接传送到下一个节点,因此应该还要连一条距离为\(0\)的边(红色),但是这条边应该连到\(k+1\)的图的下一个节点,表明我们创造了一次虫洞\(k=2\)时就不能继续往上连了)
在这里插入图片描述
最后,我们按照这样的规律,将所有的“跨层边”都连起来(彩色边,距离都为0),跑一遍最短路,\(k=0\)时的原点到\(k=u\)时的目标点的最短路就是答案
在这里插入图片描述
因此,分层图仅仅通过多加边的方式解决了最短路中的“自定义”操作,其他步骤与普通最短路无异,而如果要修改最短路算法来达到相同效果,将会是非常麻烦的


实现

那么,相信你已经掌握了分层图的思想,做一道例题试试吧
P4568 [JLOI2011] 飞行路线

这里我采用了多层图存储于同一个数组中

注意本题有个hack数据,hack原理:戳我

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int MAXN=1e4+5,MAXNK=MAXN*11;
int n,m,k,s,t,dis[MAXNK];
bool vis[MAXNK];
struct EDGE{
    int to,val;
};
vector<EDGE>g[MAXNK];
inline void add(int from,int to,int cost){
    EDGE tmp;
    tmp.to=to;tmp.val=cost;
    g[from].push_back(tmp);
}
void dijkstra(){
    dis[s]=0;
    priority_queue<pii,vector<pii>,greater<pii>>pq;
    pq.push(make_pair(0,s));
    while(!pq.empty()){
        int cur=pq.top().second;pq.pop();
        if(!vis[cur]){
            vis[cur]=1;
            for(auto nxt:g[cur]){
                if(dis[nxt.to]>dis[cur]+nxt.val){
                    dis[nxt.to]=dis[cur]+nxt.val;
                    pq.push(make_pair(dis[nxt.to],nxt.to));
                }
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    cin>>n>>m>>k>>s>>t;
    for(int i=1,from,to,cost;i<=m;i++){
        cin>>from>>to>>cost;
        add(from,to,cost);
        add(to,from,cost);
        for(int j=1;j<=k;j++){
            add(from+(j-1)*n,to+j*n,0);
            add(to+(j-1)*n,from+j*n,0);
            add(from+j*n,to+j*n,cost);
            add(to+j*n,from+j*n,cost);
        }
    }
    for(int i=1;i<=k;++i)
	{
		add(t+(i-1)*n,t+i*n,0);//这行代码的目的是为了将所有层图的目标点最短路集中在第k层的汇点,hack数据就hack在这里
	}
    dijkstra();
    cout<<dis[t+k*n]<<endl;
    return 0;
}
posted @ 2023-02-19 21:17  MessageBoxA  阅读(63)  评论(0编辑  收藏  举报