单源最短路(优先队列优化的dijkstra算法):PIPI的逃跑路线Ⅲ
问题:
思路:
本题为单源最短路问题,麻烦点在于边权会不断变化,乍一看好似非常难,我们不可能每次去更新变化后的边权,仔细观察发现边权的变化函数f(x) = 1 / (1 - x)
有点特殊,是有意设计成这样的,那么一定蕴含某个规律。设某条边的权值为x,经过一条道路后,权值变为1 / (1 - x)
,那么再经过一条道路后呢?权值将变为f(f(x)) = 1 / (1 - 1 / (1 - x) ) = (x - 1) / x
,不妨再试试,再经过一条道路后,权值变为f(f(f(x))) = 1 / (1 - (x - 1) / x) = x
,权值又变回了x,即权值的变化处于一个循环中:
当我们发现这个边权变化函数的秘密,这个题目就很好求解了,其边权只有三种可能。
所以我们在bfs时设定一个步数step:当step%3=0,两点距离就是最原始的值w;当step%3=1,两点距离变为|1/(1-w)|,由于w都是大于1的,所以也就是1/(w-1);当step%3=2,两点距离变为|1-1/w|,1减去一个小数,其值肯定大于零,所以也就是1-1/w。于是我们开个dis[3][]的数组(dis[i][j]表示,step%3为i时,源点到达j点的最短距离 ),跑一遍最短路即可。
需注意的点:
- 我们每次将节点Node置入优先队列,Node除了包含节点编号,源点到该点的距离外,还需要包含源点到该点走的步数 % 3的值,步数信息应包含在结点中,而不是在bfs中修改
- 设step表示当前遍历的结点的步数信息,distance表示源点到该点的距离,weight表示该点到邻接点的距离,
nextNode.index
表示邻接点的编号,那么进行更新最短距离的判断为weight + distance < dis[(step + 1) % 3][nextNode.index]
,注意dis数组的第一个索引为(step + 1) % 3
,而不是step
,因为从当前遍历点走到其邻接点,步数需加1
代码:
import java.util.*;
public class Main {
static final double INF = 1000000000000000000.0 + 7;
static Queue<Node> q = new PriorityQueue<>();
static double[][] dis = new double[3][100002];
static List<Node>[] adj = new ArrayList[100002];
public static void main(String[] args