分层图最短路入门
洛谷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]);
}