分层图

BZOJ 飞行路线:传送门

分层图的思想可以解决这样的问题:有n次免费的机会下的最短路问题,例如飞行路线。

题意就是一个无向图,两点之间有费用。而你有k次免费搭乘飞机的机会。这种情况下问从a点到b点的最少消费。

这种问题是不能直接用最短路解决的。可以用DP的思想,dis[n][k]数组为到n这个点用了k次优惠的最少消费。这里就不展开。

我们现在介绍一种新思路。

我们可以构造k+1张该无向图,那么第i张图和第i+1张图如何连接呢,就是用已知的边把边权变为0连在两张图的端点上。

比如已知一条边(u,v,w),我们建图的时候就可以添加这样一条边(i*n+u,(i+1)*n+v,0)。

然后我们就有了一张有n*(k+1)个点的无向图,再直接用dijkstra就可以了。

来看代码

#include<stdio.h>
#include<queue>
#include<iostream>
#define N 200010
#define WQD 2000000001
using namespace std;
int num=0,m,n,k,s,t,head[N],dis[N];

struct EDGE
{
    int w,to,next;
}e[20*N];

void add(int f,int t,int w)
{
    e[++num].next=head[f];
    e[num].w=w;
    e[num].to=t;
    head[f]=num;
}
struct VD
{
    int v,dis;
    bool operator < (const VD &a)const
    {
        return a.dis<dis;
    }
}; 
void dijkstra(int st)
{
    priority_queue <VD> q;
    for(int i=0;i<(k+1)*n;i++) dis[i]=WQD;
    dis[st]=0;
    VD temp;
    temp.v=st;temp.dis=0;
    q.push(temp);
    while(q.size())
    {
        temp=q.top();
        q.pop();
        int p=temp.v;
        if(dis[p]<temp.dis) continue;
        for(int i=head[p];i;i=e[i].next)
        {
            if(dis[e[i].to]>dis[p]+e[i].w)
            {
                dis[e[i].to]=dis[p]+e[i].w;
                temp.v=e[i].to;temp.dis=dis[e[i].to];
                q.push(temp);
            }
        }
        
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    scanf("%d%d",&s,&t);
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        for(int j=0;j<=k;j++)    //建k+1张图;
        {
            add(j*n+a,j*n+b,c);
            add(j*n+b,j*n+a,c);
        }
        for(int j=0;j<k;j++)    //建边权为0的边连接两张相邻的图;
        {
            add(j*n+a,(j+1)*n+b,0);
            add(j*n+b,(j+1)*n+a,0);
        }
    }
    dijkstra(s);
    printf("%d",dis[k*n+t]);
    return 0;
 } 

这样的操作让这种题目就变成了直接的最短路问题。

不过需要注意的是空间范围,因为我们要建立一个有n*(k+1),个点的无向图,会很大。所以一定要注意范围,不然会RE;

原文地址:https://www.cnblogs.com/xcsj/p/12264415.html