【图论基础】分层图

使用场景:经常在最短路,网络流等图论题中,通过一些规则可以修改边权而求得最优解。

理解:如果将在图上求解最短路看成是在二维平面上进行的,那么分层图的含义便是引入进行操作的次数 k 做为第三维,便可以在这个三维空间上解决问题。

每进行一次操作,除了操作的边,其他边没有任何变化,在 k=0,1,2,…,时图都是一样的,那么就将图复制成 k+1 份,第 i 层图代表进行了 i 次操作后的图。

每相邻两层图之间的联系,应该决定了一次操作是发生在哪条边上(何时进行操作)。根据操作的特点(对边权的修改)可以 i 层点到 i+1 层点的边来表示一次操作。

而这k次操作便是设立分层图的关键。

裸题

洛谷P4568

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cctype>
#include <sstream>
#define LL long long
#define _for(i,j,k) for(int i=j;i<=k;i++)
#define for_(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
const int maxn = 1e4+5;
const int maxm = 1e5+5;
const int maxk = 10+1; 
const int inf = 1e9;
struct Edge{//edge
    int v,d;
    bool operator<(const Edge& ort)const{
        return d>ort.d;
    }
};
int n,m,k,s,t;
Edge e[2*maxm*maxk];//graph
int head[maxn*maxk],net[2*maxm*maxk],cnt,dis[maxn*maxk];//邻接表
priority_queue<Edge> q;
void add_edge(int u,int v,int d){
    e[++cnt].v=v;
    e[cnt].d=d;
    net[cnt]=head[u];
    head[u]=cnt;
}
void dijkstral(int u){//dijkstral最短路模板
    _for(i,1,n*(k+1)) dis[i]=inf;
    dis[u]=0;
    q.push(Edge{u,0});
    Edge tm;
    while(!q.empty()){
        tm=q.top();
        q.pop();
        u=tm.v;
        if(dis[u]!=tm.d) continue;
        for(int i=head[u];i;i=net[i]){
            if(dis[e[i].v]>dis[u]+e[i].d){
                dis[e[i].v]=dis[u]+e[i].d;
                q.push(Edge{e[i].v,dis[e[i].v]});
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n>>m>>k>>s>>t;
    s++;t++;
    int u,v,d;
    _for(i,1,m){//建分层图
        cin>>u>>v>>d;
        u++;v++;
        add_edge(u,v,d);
        add_edge(v,u,d);
        _for(j,1,k){
            add_edge(u+j*n,v+j*n,d);//每一层
            add_edge(v+j*n,u+j*n,d);
            add_edge(u+j*n-n,v+j*n,0);//层与层之间
            add_edge(v+j*n-n,u+j*n,0);
        }
    }
    dijkstral(s);
    int an=dis[t];
    _for(i,1,k){
        an=min(an,dis[t+i*n]);//k次操作内的最短路
    }
    cout<<an;
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值