分层最短路
介绍
分层图最短路在最短路的基础上,增加了一个条件:可将 k 条边的权值变为 0.
分析
这种问题我们可以想到一种做法:
如图,我们构建一个多层的图,其中每一层都是和题目中给的图相同.(图中假设k==1)上面的层表示不将任何边的权值变为0,而下面的这一层表示使用了这个机会后的状态.
也就是0号节点可以不使用机会,转移到2号节点,也可以使用机会转移到7号(其实就是二号的镜像节点).最后我们只需要看0到4或者9的路径谁更短即可(实际上只要是正权图,一般都是下面的节点路径短)
基本原理懂了之后,就是怎么实现的问题.
暴力建图
根据刚才的讨论,我们不难发现,只要把分层图构建出来,就能直接跑dijkstra
算法,只要将每个点都拆成(k+1)个,然后将层与层之间的联系构建起来,起点是0号,终点是n*(k+1)-1
这种算法是可行的,写起来也更简洁,而且不用修改最短路的算法,只是有一些暴力.
动态规划
我们用dp
的角度去思考这个问题.我们令dp[i][j]表示从0号到i号节点,经由j次免费转移所耗费的成本
.
设会有 K
次免费机会,则 dis
的状态转移方程为:
dis[i][j]=min(dis[father][j−1],dis[father][j]+w)
其中, father
表示节点i
的父亲节点(此处表述有歧义,此转移方程发生在对边的松弛过程中,当前边是father -> i
,而使用j次机会
到i
号节点可以由father[j]
和father[j-1]
转移).当 j−1>=k
时,dis[father][j]=INF
AC代码
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <list>
#include <cstring>
#include <map>
#include <limits>
#include <cmath>
#define IN freopen("in.txt","r",stdin);
using namespace std;
typedef long long int ll;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
struct State
{
// 优先队列的结点结构体
int v, w, cnt; // cnt 表示已经使用多少次免费通行权限
State() {
}
State(int v, int w