洛谷P4568 分层图最短路

本文解析洛谷P4568分层图问题,采用DP思想结合迪杰斯特拉算法,通过二维dis数组记录不同节点及边利用次数下的最短路径,实现考虑特定边权重为0条件下的最短路径求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分层图最短路入门

洛谷P4568 分层图板子
首先讲一下题意吧,首先给n个点,m条边,然后再给两个点p,q,要求从p点走到q点时在最多省k条边的情况下所要走的最短的路程。

分析

很明显就是最短路的一种变式,但是由于有k条边可以改成路程为0,就成了这一题的难点。本蒟蒻一开始看了后不会,看了n位大佬blog后有所感悟了。
我的想法就是使用dp思想,把dis数组开成二维dis[i][j],第一维表示到达i点,第二维表示到达i点已经用掉了j次机会。
假设x将会到达i点且花费cost的价值;
则其中的状态转移方程为:dis[i][j]=min(dis[x][j],dis[x][j-1]+cost);
又考虑到最短路迪杰斯特拉是由最短的路径进行松弛的,所以dis[i][j]就会是最优解。
因为j可能会增加,也可能不变,所以需要保存两种状态:
1.dis[x][j]
2.dis[i][j-1]+cost

100分代码

#include<queue>
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define  mp make_pair
using namespace std;
struct EDGE
{
    int to,cost,next;
};
int cnt=1,head[100005];
bool vis[10005][15];
EDGE a[100005];
int dis[10005][15];
void add(int u,int v,int cost)
{
    a[cnt].to=v;
    a[cnt].cost=cost;
    a[cnt].next=head[u];
    head[u]=cnt++;
}
int main()
{
    int n,m,i,j,k;
    scanf("%d %d %d",&n,&m,&k);
    k=min(n+1,k);
    int p,q;
    scanf("%d %d",&p,&q);
    memset(head,-1,(n+1)*sizeof(int));
    for(i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        add(u,v,w);
        add(v,u,w);
    }
    memset(vis,false,sizeof(vis));
    priority_queue<pair<int,pair<int ,int > > > s;
    memset(dis,0x3f,sizeof(dis));
    s.push(mp(0,mp(p,0)));
    dis[p][0]=0;
    while(s.size())
    {
        int x=s.top().second.first,y=s.top().second.second;
        s.pop();
        if(vis[x][y]) continue;
        vis[x][y]=true;
        for(i=head[x];i>=0;i=a[i].next)
        {
            int d=a[i].cost,h=a[i].to;
            if(dis[x][y]+d<dis[h][y])
            {
                dis[h][y]=dis[x][y]+d;
                s.push(mp(-dis[h][y],mp(h,y)));//负数的最大堆等价为正数的最小堆
            }
            if(y<k&&dis[h][y+1]>dis[x][y])
            {
                dis[h][y+1]=dis[x][y];
                s.push(mp(-dis[h][y+1],mp(h,y+1)));
            }
        }
    }
    int ans=0x7fffffff;
    for(i=0;i<=k;i++) ans=min(ans,dis[q][i]);//排除部分不需要走k次即可到的,例如两点之间只有i(i<k)条边或者点不够的情况。
   	printf("%d\n",dis[q][k]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值