分层图

分层图

写在前面的费话:
分层图似乎是一个非常简单的东西,总就得被大佬看见这篇博客会笑我是个菜鸡(不过没关系,反正实锤是菜鸡了orz)。不过最近发现以前学的东西和做过的题老是很快就忘了,所以觉得还是该记录一下。

适用场景:
一些图论题,比如最短路、网络流等,题目对边的权值提供可选的操作,比如可以将一定数量的边权减半,在此基础上求解最优解。

算法思路:
根据是否进行题目提供的操作以及操作次数的不同,会产生非常多的情况,如果考虑何时使用操作,情况更是多。如果将在图上求解最短路看成是在二维平面上进行的,引入进行操作的次数 k 做为第三维,那么这个三维空间就理应可以包含所有的情况,便可以在这个三维空间上解决问题。

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

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

例如:有带权边 = w, 可选操作:修改权值为0
分层图_第1张图片

那么对于分层图的构建步骤可以描述为:
1、先将图复制成 k+1 份 (0 ~ k)
2、对于图中的每一条边 从 ui 到 vi+1 建立与题目所给操作相对应的边(i=0,1,…,k)

k代表了进行操作的次数,而每层之间点的关系代表了何时进行操作。
分层图示意图:
分层图_第2张图片
无向图一样处理,因为可以完全看成有向图。
时间复杂度:O(k*(m+n)log(n)) 吧

一个裸题:
洛谷P4568
题目描述
Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n−1,一共有m种航线,每种航线连接两个城市,并且航线有一定的价格。
Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?
输入格式:
数据的第一行有三个整数,n,m,k,分别表示城市数,航线数和免费乘坐次数。
第二行有两个整数,s,t,分别表示他们出行的起点城市编号和终点城市编号。
接下来有m行,每行三个整数,a,b,c,表示存在一种航线,能从城市a到达城市b,或从城市b到达城市a,价格为c。
输出格式:
只有一行,包含一个整数,为最少花费。
输入样例:

5 6 1
0 4
0 1 5
1 2 5
2 3 5
3 4 5
2 3 3
0 2 100

输出样例:

8

AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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;
}

类似的题:
洛谷P2939

你可能感兴趣的:(图论)