4 6
1 2 2
1 3 3
1 4 8
2 3 4
2 4 5
3 4 3
0
1 3
2 3 4
0
3 1
1 2 3
0
1 3
0
⭐ 题目分析
① 不含重边自环的无向图(边得开节点数的两倍),而且没有负权边(dijkstra),n = m 稀疏图
② 求的是最短时间,时间问题转换为路径问题,多维护了个延误时间
③ 数据范围 10^5 大致也就是 O(n logn)的算法 -> 堆优化版的dijkstra
⭐ 延误时间
怎么求一个达到一个点要后的最小离开时间呢?
① 开一个 ArrayList 的数组,存每一个点的禁止离开的时间
② 假设到某点 x 的时间是 t ,直接枚举 x点的 ArraysList ,如果遍历完没有大于等于 t 的禁时 k,那就直接返回 t;
有 k == t, t++; 直到找到 第一个 k > t 的,return t ;(t 就是当前时间到达某点的最小可以离开的时间)
③ 关于时间复杂度,由于 dijkstra 算法的特性,判重数组使得个点只会算一次,而且所有 k[i] 相加的和不超过 10^5
(经验之谈:当出现这种提示的时候,这点大概率不是关键点,暴力枚举的复杂度不会太坏)
⭐ 最短路径
多了延误时间,每次取最小权值的边还能得到最小路径吗?
设到达某点的时间为 t,则离开时间 l >= t,
当 t 最小时,l 才有可能小
反之,当 t2 > t , l2 >= l ,最坏的情况也就是相等,最短路径依旧成立
import java.io.*;
import java.util.*;
public class Main
{
static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
static int N = 100010, M = 2 * N, n, m;
static ArrayList<Integer>[] K = new ArrayList[N];
// 邻接表存图
static int[] h = new int[N];
static int[] e = new int[M];
static int[] ne = new int[M];
static int[] W = new int[M];
static int[] dist = new int[N];
static boolean[] st = new boolean[N];
static int idx;
private static void add(int a, int b, int c)
{
e[idx] = b;
ne[idx] = h[a];
W[idx] = c;
h[a] = idx++;
}
public static void main(String[] args) throws IOException
{
String[] split = in.readLine().split(" ");
n = Integer.parseInt(split[0]);
m = Integer.parseInt(split[1]);
Arrays.fill(h, -1);
for (int i = 0; i < m; i++)
{
String[] split2 = in.readLine().split(" ");
int a = Integer.parseInt(split2[0]);
int b = Integer.parseInt(split2[1]);
int c = Integer.parseInt(split2[2]);
add(a, b, c);
add(b, a, c);
}
// 切记要初始化 ArrayList 的数组,不然就是空指针
for (int i = 0; i <= n; i++)
K[i] = new ArrayList<>();
for (int i = 1; i <= n; i++)
{
String[] split2 = in.readLine().split(" ");
int k = Integer.parseInt(split2[0]);
for (int j = 1; j <= k; j++)//注意:此处得从 1 号开始读,0 号点已经被读了
{
K[i].add(Integer.parseInt(split2[j]));
}
}
dijkStra();
if (dist[n] == 0x3f3f3f3f)
System.out.println("-1");
else
System.out.println(dist[n]);
}
static class Pair
{
int x;// 到达此点的时间
int y;// 此点的值
public Pair(int x, int y)
{
super();
this.x = x;
this.y = y;
}
}
private static void dijkStra()
{
PriorityQueue<Pair> heap = new PriorityQueue<>((o1, o2) -> o1.x - o2.x);
heap.add(new Pair(0, 1));
Arrays.fill(dist, 0x3f3f3f3f);
dist[1] = 0;
while (heap.size() != 0)
{
Pair t = heap.poll();
int ver = t.y;
if (st[ver])
continue;
st[ver] = true;
int distance = getDelay(ver, t.x);
for (int i = h[ver]; i != -1; i = ne[i])
{
int j = e[i];// j 是当前节点指向的节点
if (dist[j] > distance + W[i])
{
dist[j] = distance + W[i];
heap.add(new Pair(dist[j], j));
}
}
}
}
private static int getDelay(int u, int time)
{
for (int t : K[u])
{
if (t == time)// 刚好碰上禁出的日子 延误时间+1
time++;
else if (t > time)// 反正碰见第一个大于 当前time的就 break
break;
}
return time;
}
}