AcWing 4871. 最早时刻 java 单源最短路 dijkstra 谁看谁都会

在这里插入图片描述在这里插入图片描述

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;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值