图论——分层图最短路

概述

分层图最短路,如:有 k 次零代价通过一条路径,求总的最小花费。对于这种题目,我们可以采用 DP 相关的思想,设 disi,j表示当前从起点 i 号结点,使用了 j 次免费通行权限后的最短路径。显然,dis 数组可以这么转移:

disi,j=min(min(disv,j-1if(jk)),min(disv,j+w),disi,j)

其中v表是与i相邻的节点,w表示经过这条边的边权,特别地,如果j>k,那么disv,j=

概念理解:分层图最短路往往是与DP思想结合的体现,它适用于求最短路性质的问题但加了额外限制,在考场上如果要想作对做这类题,必须先发现这道题隐藏条件和题目意思与最短路有关(不要小看这一步,因为往往最开始就没想到这样做,导致后面的结果是错误的),再思考怎样对题目进行图论建模。

例题

1

Telephone Lines

简单来说,本题是在无向图上求出一条从1N的路径,使路径上第K+1大的边权尽量小。

可以仿照前面所说的方法,用D[x,p]表示从1号节点到达基站x,途中已经指定了p条电缆免费时,经过的路径最贵的电缆的话费最小是多少(也就是选择一条从1x的路径,使路径上第p+1大的边权尽量少)。若有一条从xy长度为z的无向边,则应该用max(D[x,p],z)更新D[y,p]的最小值,用D[x,p]更新D[y,p+1]的最小值。前者表示不在电缆(x,y,z)上使用免费升级服务,后者表示使用。

显然,我们刚才设计的状态转移是有后效性的(因为本题是按照动态规划的思想解决的,动态规划对状态空间的遍历构成一张有向无环图。注意一定是有向无环图!遍历顺序就是该有向无环图的一个拓扑序。 有向无向图中的节点对应问题中的“状态”,图中的边对应状态之间的“转移”,转移的选取就是动态规划中的“决策”。 在本题中,比如有三个点 a,b,c,构成一个环,那么用dijkstra更新就会发生当前更新的值不是最终的值,那么转移时就会出现问题。)。在有后效性时,一种解决方案是利用迭代思想,借助SPFA算法进行动态规划, 直至所有状态收敛(不能再更新)。

从最短路径问题的角度去理解,图中的节点也不仅限于“整数编号”,可以扩展到二维,用二元组(x,p)代表一个节点,从(x,p)(y,p)有长度为z的边,从(x,p)(y,p+1)有长度为0的边。D[x,p]表示起点(1,0)到节点(x,p),路径上最长的边最短是多少。这是NK个点,PK条边的广义最短路径问题。对于非精心构造的数据,SPFA算法的时间复杂度为O(tNP),其中t为常数,实际测试可以AC。本题也让我们进一步领会了动态规划与最短路的共通性。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,p,k;
const int N=1000000+10,M=10000000+10;
int head[N],to[M],nxt[M],cnt,w[M],dis[N];
struct node{
    int val,pos;
    bool operator >(const node &x)const{
        return val>x.val;
    }
};
void add(int u,int v,int f){
    to[++cnt]=v;
    w[cnt]=f;
    nxt[cnt]=head[u];
    head[u]=cnt;
    return;
}
priority_queue<node,vector<node>,greater<node> >q;
bool v[N];
void dijkstra(){
    memset(dis,0x3f,sizeof(dis));
    dis[1]=0;
    q.push(node{0,1});
    while(!q.empty()){
        node t=q.top();q.pop();
        if(v[t.pos])continue;
        v[t.pos]=1;
        for(int i=head[t.pos];i;i=nxt[i]){
            int v=to[i],z=max(dis[t.pos],w[i]);
            if(dis[v]>z){
                dis[v]=z;
                q.push(node{dis[v],v});
            }
        }
    }
    return;
}
signed main(){
    scanf("%lld %lld %lld",&n,&p,&k);
    for(int i=1,x,y,z;i<=p;i++){
        scanf("%lld %lld %lld",&x,&y,&z);
        add(x,y,z);add(y,x,z);
        for(int j=1;j<=k;j++){
            add(x+(j-1)*n,y+j*n,0);
            add(y+(j-1)*n,x+j*n,0);
            add(x+j*n,y+j*n,z);
            add(y+j*n,x+j*n,z);
        }
    }
    dijkstra();
    int ans=1e18;
    for(int i=0;i<=k;i++){
        ans=min(ans,dis[n+i*n]);
    }
    if(ans==1e18)printf("-1\n");
    else printf("%lld\n",ans);
    return 0;
}

2

飞行路线

由于购买机票不需要花钱,所以肯定不会多次重复乘坐同样的航线或者多次访问到同一个城市。如果k=0本题就是最基础的最短路问题。但题目中说明了对有限条边设置为免费),可以使用分层图的方式,将图多复制k次,原编号为i的节点复制为编号i+jn(1jk)的节点,然后对于原图存在的边,第j层和第j+1层的对应节点也需要连上,看起来就是相同的图上下堆叠起来。

代码如下

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+50,M=2200050;
int head[N*12],cnt,nxt[M],to[M],w[M],n,m,k,s,t,dis[N*12];
void add(int u,int v,int f)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    w[cnt]=f;
    head[u]=cnt;
} 
struct node
{
    int val,pos;
    bool operator >(const node &x)const{
        return val>x.val;
    }
};
priority_queue<node,vector<node>,greater<node> >q;
bool b[N*12];
void dij()
{
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    q.push(node{dis[s],s});
    while(!q.empty())
    {
        node t=q.top();q.pop();
        if(b[t.pos])continue;
        b[t.pos]=1;
        for(int i=head[t.pos];i;i=nxt[i])
        {
            int v=to[i];
            if(dis[v]>dis[t.pos]+w[i])
            {
                dis[v]=dis[t.pos]+w[i];
                q.push(node{dis[v],v});
            }
        }
    }
    return;
}
int main()
{
    scanf("%d %d %d",&n,&m,&k);
    scanf("%d %d",&s,&t);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d %d %d",&u,&v,&w);
        add(u,v,w);add(v,u,w);
        for(int j=1;j<=k;j++)
        {
            add(u+j*n,v+j*n,w);
            add(v+j*n,u+j*n,w);
            add(u+j*n-n,v+j*n,0);
            add(v+j*n-n,u+j*n,0);
        }
    }
    dij();
    int ans=0x3f3f3f3f;
    for(int j=0;j<=k;j++)
        if(dis[t+j*n]<ans)
            ans=dis[t+j*n];
    printf("%d\n",ans);
    return 0;
}
posted @   wangyuanbo  阅读(200)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示