class064 Dijkstra算法、分层图最短路【算法】

news2024/11/16 8:56:25

class064 Dijkstra算法、分层图最短路【算法】

算法讲解064【必备】Dijkstra算法、分层图最短路
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

code1 743. 网络延迟时间

// Dijkstra算法模版(Leetcode)
// 网络延迟时间
// 有 n 个网络节点,标记为 1 到 n
// 给你一个列表 times,表示信号经过 有向 边的传递时间
// times[i] = (ui, vi, wi),表示从ui到vi传递信号的时间是wi
// 现在,从某个节点 s 发出一个信号
// 需要多久才能使所有节点都收到信号
// 如果不能使所有节点收到信号,返回 -1
// 测试链接 : https://leetcode.cn/problems/network-delay-time

code1 P4779 【模板】单源最短路径(标准版)

// Dijkstra算法模版(洛谷)
// 静态空间实现 : 链式前向星 + 反向索引堆
// 测试链接 : https://www.luogu.com.cn/problem/P4779
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码,把主类名改成Main,可以直接通过

package class064;

// Dijkstra算法模版(洛谷)
// 静态空间实现 : 链式前向星 + 反向索引堆
// 测试链接 : https://www.luogu.com.cn/problem/P4779
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码,把主类名改成Main,可以直接通过

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Arrays;

public class Code01_DijkstraLuogu {

	public static int MAXN = 100001;

	public static int MAXM = 200001;

	// 链式前向星
	public static int[] head = new int[MAXN];

	public static int[] next = new int[MAXM];

	public static int[] to = new int[MAXM];

	public static int[] weight = new int[MAXM];

	public static int cnt;

	// 反向索引堆
	public static int[] heap = new int[MAXN];

	// where[v] = -1,表示v这个节点,从来没有进入过堆
	// where[v] = -2,表示v这个节点,已经弹出过了
	// where[v] = i(>=0),表示v这个节点,在堆上的i位置
	public static int[] where = new int[MAXN];

	public static int heapSize;

	public static int[] distance = new int[MAXN];

	public static int n, m, s;

	public static void build() {
		cnt = 1;
		heapSize = 0;
		Arrays.fill(head, 1, n + 1, 0);
		Arrays.fill(where, 1, n + 1, -1);
		Arrays.fill(distance, 1, n + 1, Integer.MAX_VALUE);
	}

	// 链式前向星建图
	public static void addEdge(int u, int v, int w) {
		next[cnt] = head[u];
		to[cnt] = v;
		weight[cnt] = w;
		head[u] = cnt++;
	}

	public static void addOrUpdateOrIgnore(int v, int w) {
		if (where[v] == -1) {
			heap[heapSize] = v;
			where[v] = heapSize++;
			distance[v] = w;
			heapInsert(where[v]);
		} else if (where[v] >= 0) {
			distance[v] = Math.min(distance[v], w);
			heapInsert(where[v]);
		}
	}

	public static void heapInsert(int i) {
		while (distance[heap[i]] < distance[heap[(i - 1) / 2]]) {
			swap(i, (i - 1) / 2);
			i = (i - 1) / 2;
		}
	}

	public static int pop() {
		int ans = heap[0];
		swap(0, --heapSize);
		heapify(0);
		where[ans] = -2;
		return ans;
	}

	public static void heapify(int i) {
		int l = 1;
		while (l < heapSize) {
			int best = l + 1 < heapSize && distance[heap[l + 1]] < distance[heap[l]] ? l + 1 : l;
			best = distance[heap[best]] < distance[heap[i]] ? best : i;
			if (best == i) {
				break;
			}
			swap(best, i);
			i = best;
			l = i * 2 + 1;
		}
	}

	public static boolean isEmpty() {
		return heapSize == 0;
	}

	public static void swap(int i, int j) {
		int tmp = heap[i];
		heap[i] = heap[j];
		heap[j] = tmp;
		where[heap[i]] = i;
		where[heap[j]] = j;
	}

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval;
			in.nextToken(); m = (int) in.nval;
			in.nextToken(); s = (int) in.nval;
			build();
			for (int i = 0, u, v, w; i < m; i++) {
				in.nextToken(); u = (int) in.nval;
				in.nextToken(); v = (int) in.nval;
				in.nextToken(); w = (int) in.nval;
				addEdge(u, v, w);
			}
			dijkstra();
			out.print(distance[1]);
			for (int i = 2; i <= n; i++) {
				out.print(" " + distance[i]);
			}
			out.println();
		}
		out.flush();
		out.close();
		br.close();
	}

	public static void dijkstra() {
		addOrUpdateOrIgnore(s, 0);
		while (!isEmpty()) {
			int v = pop();
			for (int ei = head[v]; ei > 0; ei = next[ei]) {
				addOrUpdateOrIgnore(to[ei], distance[v] + weight[ei]);
			}
		}
	}

}

code2 1631. 最小体力消耗路径

// 最小体力消耗路径
// 你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights
// 其中 heights[row][col] 表示格子 (row, col) 的高度
// 一开始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1)
// (注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动
// 你想要找到耗费 体力 最小的一条路径
// 一条路径耗费的体力值是路径上,相邻格子之间高度差绝对值的最大值
// 请你返回从左上角走到右下角的最小 体力消耗值
// 测试链接 :https://leetcode.cn/problems/path-with-minimum-effort/

package class064;

import java.util.PriorityQueue;

// 最小体力消耗路径
// 你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights
// 其中 heights[row][col] 表示格子 (row, col) 的高度
// 一开始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1) 
// (注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动
// 你想要找到耗费 体力 最小的一条路径
// 一条路径耗费的体力值是路径上,相邻格子之间高度差绝对值的最大值
// 请你返回从左上角走到右下角的最小 体力消耗值
// 测试链接 :https://leetcode.cn/problems/path-with-minimum-effort/
public class Code02_PathWithMinimumEffort {

	// 0:上,1:右,2:下,3:左
	public static int[] move = new int[] { -1, 0, 1, 0, -1 };

	public int minimumEffortPath(int[][] heights) {
		// (0,0)源点
		// -> (x,y)
		int n = heights.length;
		int m = heights[0].length;
		int[][] distance = new int[n][m];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				distance[i][j] = Integer.MAX_VALUE;
			}
		}
		distance[0][0] = 0;
		boolean[][] visited = new boolean[n][m];
		// 0 : 格子的行
		// 1 : 格子的列
		// 2 : 源点到当前格子的代价
		PriorityQueue<int[]> heap = new PriorityQueue<int[]>((a, b) -> a[2] - b[2]);
		heap.add(new int[] { 0, 0, 0 });
		while (!heap.isEmpty()) {
			int[] record = heap.poll();
			int x = record[0];
			int y = record[1];
			int c = record[2];
			if (visited[x][y]) {
				continue;
			}
			if (x == n - 1 && y == m - 1) {
				// 常见剪枝
				// 发现终点直接返回
				// 不用等都结束
				return c;
			}
			visited[x][y] = true;
			for (int i = 0; i < 4; i++) {
				int nx = x + move[i];
				int ny = y + move[i + 1];
				if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny]) {
					int nc = Math.max(c, Math.abs(heights[x][y] - heights[nx][ny]));
					if (nc < distance[nx][ny]) {
						distance[nx][ny] = nc;
						heap.add(new int[] { nx, ny, nc });
					}
				}
			}
		}
		return -1;
	}

}

code3 778. 水位上升的泳池中游泳

// 水位上升的泳池中游泳
// 在一个 n x n 的整数矩阵 grid 中
// 每一个方格的值 grid[i][j] 表示位置 (i, j) 的平台高度
// 当开始下雨时,在时间为 t 时,水池中的水位为 t
// 你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台
// 假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的
// 当然,在你游泳的时候你必须待在坐标方格里面。
// 你从坐标方格的左上平台 (0,0) 出发
// 返回 你到达坐标方格的右下平台 (n-1, n-1) 所需的最少时间
// 测试链接 : https://leetcode.cn/problems/swim-in-rising-water/

package class064;

import java.util.PriorityQueue;

// 水位上升的泳池中游泳
// 在一个 n x n 的整数矩阵 grid 中
// 每一个方格的值 grid[i][j] 表示位置 (i, j) 的平台高度
// 当开始下雨时,在时间为 t 时,水池中的水位为 t
// 你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台
// 假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的
// 当然,在你游泳的时候你必须待在坐标方格里面。
// 你从坐标方格的左上平台 (0,0) 出发
// 返回 你到达坐标方格的右下平台 (n-1, n-1) 所需的最少时间
// 测试链接 : https://leetcode.cn/problems/swim-in-rising-water/
public class Code03_SwimInRisingWater {

	// 0:上,1:右,2:下,3:左
	public static int[] move = new int[] { -1, 0, 1, 0, -1 };

	public static int swimInWater(int[][] grid) {
		int n = grid.length;
		int m = grid[0].length;
		int[][] distance = new int[n][m];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				distance[i][j] = Integer.MAX_VALUE;
			}
		}
		distance[0][0] = grid[0][0];
		boolean[][] visited = new boolean[n][m];
		// 0 : 格子的行
		// 1 : 格子的列
		// 2 : 源点到当前格子的代价
		PriorityQueue<int[]> heap = new PriorityQueue<>((a, b) -> a[2] - b[2]);
		heap.add(new int[] { 0, 0, grid[0][0] });
		while (!heap.isEmpty()) {
			int x = heap.peek()[0];
			int y = heap.peek()[1];
			int c = heap.peek()[2];
			heap.poll();
			if (visited[x][y]) {
				continue;
			}
			visited[x][y] = true;
			if (x == n - 1 && y == m - 1) {
				// 常见剪枝
				// 发现终点直接返回
				// 不用等都结束
				return c;
			}
			for (int i = 0, nx, ny, nc; i < 4; i++) {
				nx = x + move[i];
				ny = y + move[i + 1];
				if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny]) {
					nc = Math.max(c, grid[nx][ny]);
					if (nc < distance[nx][ny]) {
						distance[nx][ny] = nc;
						heap.add(new int[] { nx, ny, nc });
					}
				}
			}
		}
		return -1;
	}

}

code4 864. 获取所有钥匙的最短路径

// 获取所有钥匙的最短路径
// 给定一个二维网格 grid ,其中:
// ‘.’ 代表一个空房间、‘#’ 代表一堵、‘@’ 是起点
// 小写字母代表钥匙、大写字母代表锁
// 从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间
// 不能在网格外面行走,也无法穿过一堵墙
// 如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
// 假设 k 为 钥匙/锁 的个数,且满足 1 <= k <= 6,
// 字母表中的前 k 个字母在网格中都有自己对应的一个小写和一个大写字母
// 换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁
// 另外,代表钥匙和锁的字母互为大小写并按字母顺序排列
// 返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
// 测试链接:https://leetcode.cn/problems/shortest-path-to-get-all-keys

package class064;

// 获取所有钥匙的最短路径
// 给定一个二维网格 grid ,其中:
// '.' 代表一个空房间、'#' 代表一堵、'@' 是起点
// 小写字母代表钥匙、大写字母代表锁
// 从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间
// 不能在网格外面行走,也无法穿过一堵墙
// 如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
// 假设 k 为 钥匙/锁 的个数,且满足 1 <= k <= 6,
// 字母表中的前 k 个字母在网格中都有自己对应的一个小写和一个大写字母
// 换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁
// 另外,代表钥匙和锁的字母互为大小写并按字母顺序排列
// 返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
// 测试链接:https://leetcode.cn/problems/shortest-path-to-get-all-keys
public class Code04_ShortestPathToGetAllKeys {

	public static int MAXN = 31;

	public static int MAXM = 31;

	public static int MAXK = 6;

	// 0:上,1:右,2:下,3:左
	public static int[] move = new int[] { -1, 0, 1, 0, -1 };

	public static char[][] grid = new char[MAXN][];

	public static boolean[][][] visited = new boolean[MAXN][MAXM][1 << MAXK];

	// 0 : 行
	// 1 : 列
	// 2 : 收集钥匙的状态
	public static int[][] queue = new int[MAXN * MAXM * (1 << MAXK)][3];

	public static int l, r, n, m, key;

	public static void build(String[] g) {
		l = r = key = 0;
		n = g.length;
		m = g[0].length();
		for (int i = 0; i < n; i++) {
			grid[i] = g[i].toCharArray();
		}
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if (grid[i][j] == '@') {
					queue[r][0] = i;
					queue[r][1] = j;
					// 0 : 000000
					queue[r++][2] = 0;
				}
				if (grid[i][j] >= 'a' && grid[i][j] <= 'f') {
					key |= 1 << (grid[i][j] - 'a');
				}
			}
		}
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				for (int s = 0; s <= key; s++) {
					visited[i][j][s] = false;
				}
			}
		}
	}

	public static int shortestPathAllKeys(String[] g) {
		build(g);
		int level = 1;
		while (l < r) {
			for (int k = 0, size = r - l, x, y, s; k < size; k++) {
				x = queue[l][0];
				y = queue[l][1];
				s = queue[l++][2];
				for (int i = 0, nx, ny, ns; i < 4; i++) {
					nx = x + move[i];
					ny = y + move[i + 1];
					ns = s;
					if (nx < 0 || nx == n || ny < 0 || ny == m || grid[nx][ny] == '#') {
						// 越界或者障碍
						continue;
					}
					if (grid[nx][ny] >= 'A' && grid[nx][ny] <= 'F' && ((ns & (1 << (grid[nx][ny] - 'A'))) == 0)) {
						// 是锁,又没有对应的钥匙
						continue;
					}
					if (grid[nx][ny] >= 'a' && grid[nx][ny] <= 'f') {
						// 是某一把钥匙
						ns |= (1 << (grid[nx][ny] - 'a'));
					}
					if (ns == key) {
						// 常见剪枝
						// 发现终点直接返回
						// 不用等都结束
						return level;
					}
					if (!visited[nx][ny][ns]) {
						visited[nx][ny][ns] = true;
						queue[r][0] = nx;
						queue[r][1] = ny;
						queue[r++][2] = ns;
					}
				}
			}
			level++;
		}
		return -1;
	}

}

code5 LCP 35. 电动车游城市

// 电动车游城市
// 小明的电动车电量充满时可行驶距离为 cnt,每行驶 1 单位距离消耗 1 单位电量,且花费 1 单位时间
// 小明想选择电动车作为代步工具。地图上共有 N 个景点,景点编号为 0 ~ N-1
// 他将地图信息以 [城市 A 编号,城市 B 编号,两城市间距离] 格式整理在在二维数组 paths,
// 表示城市 A、B 间存在双向通路。
// 初始状态,电动车电量为 0。每个城市都设有充电桩,
// charge[i] 表示第 i 个城市每充 1 单位电量需要花费的单位时间。
// 请返回小明最少需要花费多少单位时间从起点城市 start 抵达终点城市 end
// 测试链接 : https://leetcode.cn/problems/DFPeFJ/

package class064;

import java.util.ArrayList;
import java.util.PriorityQueue;

// 电动车游城市
// 小明的电动车电量充满时可行驶距离为 cnt,每行驶 1 单位距离消耗 1 单位电量,且花费 1 单位时间
// 小明想选择电动车作为代步工具。地图上共有 N 个景点,景点编号为 0 ~ N-1
// 他将地图信息以 [城市 A 编号,城市 B 编号,两城市间距离] 格式整理在在二维数组 paths,
// 表示城市 A、B 间存在双向通路。
// 初始状态,电动车电量为 0。每个城市都设有充电桩,
// charge[i] 表示第 i 个城市每充 1 单位电量需要花费的单位时间。
// 请返回小明最少需要花费多少单位时间从起点城市 start 抵达终点城市 end
// 测试链接 : https://leetcode.cn/problems/DFPeFJ/
public class Code05_VisitCityMinCost {

	// 电动车总电量,cnt
	public static int electricCarPlan(int[][] paths, int cnt, int start, int end, int[] charge) {
		int n = charge.length;
		ArrayList<ArrayList<int[]>> graph = new ArrayList<>();
		for (int i = 0; i < n; i++) {
			graph.add(new ArrayList<>());
		}
		for (int[] path : paths) {
			graph.get(path[0]).add(new int[] { path[1], path[2] });
			graph.get(path[1]).add(new int[] { path[0], path[2] });
		}
		// n : 0 ~ n-1,不代表图上的点
		// (点,到达这个点的电量)图上的点!
		int[][] distance = new int[n][cnt + 1];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j <= cnt; j++) {
				distance[i][j] = Integer.MAX_VALUE;
			}
		}
		distance[start][0] = 0;
		boolean[][] visited = new boolean[n][cnt + 1];
		// 0 : 当前点
		// 1 : 来到当前点的电量
		// 2 : 花费时间
		PriorityQueue<int[]> heap = new PriorityQueue<int[]>((a, b) -> (a[2] - b[2]));
		heap.add(new int[] { start, 0, 0 });
		while (!heap.isEmpty()) {
			int[] record = heap.poll();
			int cur = record[0];
			int power = record[1];
			int cost = record[2];
			if (visited[cur][power]) {
				continue;
			}
			if (cur == end) {
				// 常见剪枝
				// 发现终点直接返回
				// 不用等都结束
				return cost;
			}
			visited[cur][power] = true;
			if (power < cnt) {
				// 充一格电
				// cur, power+1
				if (!visited[cur][power + 1] && cost + charge[cur] < distance[cur][power + 1]) {
					distance[cur][power + 1] = cost + charge[cur];
					heap.add(new int[] { cur, power + 1, cost + charge[cur] });
				}
			}
			for (int[] edge : graph.get(cur)) {
				// 不充电去别的城市
				int nextCity = edge[0];
				int restPower = power - edge[1];
				int nextCost = cost + edge[1];
				if (restPower >= 0 && !visited[nextCity][restPower] && nextCost < distance[nextCity][restPower]) {
					distance[nextCity][restPower] = nextCost;
					heap.add(new int[] { nextCity, restPower, nextCost });
				}
			}
		}
		return -1;
	}

}

code6 P4568 [JLOI2011] 飞行路线

// 飞行路线(语言提供的堆)
// Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司
// 该航空公司一共在n个城市设有业务,设这些城市分别标记为0 ~ n−1
// 一共有m种航线,每种航线连接两个城市,并且航线有一定的价格
// Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机
// 航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机
// 那么 Alice 和 Bob 这次出行最少花费多少
// 测试链接 : https://www.luogu.com.cn/problem/P4568
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码,把主类名改成Main,可以直接通过

code6 P4568 [JLOI2011] 飞行路线

// 飞行路线(自己手撸的堆)
// Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司
// 该航空公司一共在n个城市设有业务,设这些城市分别标记为0 ~ n−1
// 一共有m种航线,每种航线连接两个城市,并且航线有一定的价格
// Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机
// 航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机
// 那么 Alice 和 Bob 这次出行最少花费多少
// 测试链接 : https://www.luogu.com.cn/problem/P4568
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码,把主类名改成Main,可以直接通过

package class064;

// 飞行路线(自己手撸的堆)
// Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司
// 该航空公司一共在n个城市设有业务,设这些城市分别标记为0 ~ n−1
// 一共有m种航线,每种航线连接两个城市,并且航线有一定的价格
// Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机
// 航空公司对他们这次旅行也推出优惠,他们可以免费在最多k种航线上搭乘飞机
// 那么 Alice 和 Bob 这次出行最少花费多少
// 测试链接 : https://www.luogu.com.cn/problem/P4568
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码,把主类名改成Main,可以直接通过

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;

public class Code06_FlightPath2 {

	public static int MAXN = 10001;

	public static int MAXM = 100001;

	public static int MAXK = 11;

	// 链式前向星建图需要
	public static int[] head = new int[MAXN];

	public static int[] next = new int[MAXM];

	public static int[] to = new int[MAXM];

	public static int[] weight = new int[MAXM];

	public static int cnt;

	// Dijkstra需要
	public static int[][] distance = new int[MAXN][MAXK];

	public static boolean[][] visited = new boolean[MAXN][MAXK];

	// 自己写的普通堆,静态结构,推荐
	// 注意是自己写的普通堆,不是反向索引堆
	// 因为(点编号,使用免费路线的次数),两个参数的组合才是图中的点
	// 两个参数的组合对应一个点(一个堆的下标),所以反向索引堆不好写
	// 其实也能实现,二维点变成一维下标即可
	// 但是会造成很多困惑,索性算了,就手写普通堆吧
	public static int[][] heap = new int[MAXM * MAXK][3];

	public static int heapSize;

	public static int n, m, k, s, t;

	public static void build() {
		cnt = 1;
		heapSize = 0;
		for (int i = 0; i < n; i++) {
			head[i] = 0;
			for (int j = 0; j <= k; j++) {
				distance[i][j] = Integer.MAX_VALUE;
				visited[i][j] = false;
			}
		}
	}

	public static void addEdge(int u, int v, int w) {
		next[cnt] = head[u];
		to[cnt] = v;
		weight[cnt] = w;
		head[u] = cnt++;
	}

	public static void push(int u, int t, int c) {
		heap[heapSize][0] = u;
		heap[heapSize][1] = t;
		heap[heapSize][2] = c;
		int i = heapSize++;
		while (heap[i][2] < heap[(i - 1) / 2][2]) {
			swap(i, (i - 1) / 2);
			i = (i - 1) / 2;
		}
	}

	public static int u, use, cost;

	public static void pop() {
		u = heap[0][0];
		use = heap[0][1];
		cost = heap[0][2];
		swap(0, --heapSize);
		heapify();
	}

	public static void heapify() {
		int i = 0;
		int l = 1;
		while (l < heapSize) {
			int best = l + 1 < heapSize && heap[l + 1][2] < heap[l][2] ? l + 1 : l;
			best = heap[best][2] < heap[i][2] ? best : i;
			if (best == i) {
				break;
			}
			swap(best, i);
			i = best;
			l = i * 2 + 1;
		}
	}

	public static void swap(int i, int j) {
		int[] tmp = heap[i];
		heap[i] = heap[j];
		heap[j] = tmp;
	}

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StreamTokenizer in = new StreamTokenizer(br);
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		while (in.nextToken() != StreamTokenizer.TT_EOF) {
			n = (int) in.nval;
			in.nextToken(); m = (int) in.nval;
			in.nextToken(); k = (int) in.nval;
			in.nextToken(); s = (int) in.nval;
			in.nextToken(); t = (int) in.nval;
			build();
			for (int i = 0, a, b, c; i < m; i++) {
				in.nextToken(); a = (int) in.nval;
				in.nextToken(); b = (int) in.nval;
				in.nextToken(); c = (int) in.nval;
				addEdge(a, b, c);
				addEdge(b, a, c);
			}
			out.println(dijkstra());
		}
		out.flush();
		out.close();
		br.close();
	}

	public static int dijkstra() {
		distance[s][0] = 0;
		push(s, 0, 0);
		while (heapSize > 0) {
			pop();
			if (visited[u][use]) {
				continue;
			}
			visited[u][use] = true;
			if (u == t) {
				// 常见剪枝
				// 发现终点直接返回
				// 不用等都结束
				return cost;
			}
			for (int ei = head[u], v, w; ei > 0; ei = next[ei]) {
				v = to[ei];
				w = weight[ei];
				if (use < k && distance[v][use + 1] > distance[u][use]) {
					// 使用免费
					distance[v][use + 1] = distance[u][use];
					push(v, use + 1, distance[v][use + 1]);
				}
				if (distance[v][use] > distance[u][use] + w) {
					// 不用免费
					distance[v][use] = distance[u][use] + w;
					push(v, use, distance[v][use]);
				}
			}
		}
		return -1;
	}

}

2023-12-9 19:21:42

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1299410.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一个或多个筛选器或者Listeners启动失败 的问题

核心&#xff1a; 这个就是有好多情况会导致这个问题&#xff0c;像是文件找不到&#xff0c;缺少jar包等原因&#xff0c;还是要看报错的具体信息。 报错情况&#xff1a; 一个或多个listeners启动失败&#xff0c;更多详细信息查看对应的容器日志文件 由于之前的错误&#x…

Flutter视频播放器在iOS端和Android端都能实现全屏播放

Flutter开发过程中&#xff0c;对于视频播放的三方组件有很多&#xff0c;在Android端适配都挺好&#xff0c;但是在适配iPhone手机的时候&#xff0c;如果设置了UIInterfaceOrientationLandscapeLeft和UIInterfaceOrientationLandscapeRight都为false的情况下&#xff0c;无法…

基于lambda简化设计模式

前言 虽说使用设计模式可以让复杂的业务代码变得清晰且易于维护&#xff0c;但是某些情况下&#xff0c;开发可能会遇到我为了简单的业务逻辑去适配设计模式的情况&#xff0c;本文笔者就以四种常见的设计模式为例&#xff0c;演示如何基于lambda来简化设计模式的实现。 策略…

postgresql自带指令命令系列二

简介 在安装postgresql数据库的时候会需要设置一个关于postgresql数据库的PATH变量 export PATH/home/postgres/pg/bin:$PATH&#xff0c;该变量会指向postgresql安装路径下的bin目录。这个安装目录和我们在进行编译的时候./configure --prefix [指定安装目录] 中的prefix参…

TypeScript中的单件设计模式

基本概念 &#xff08;1&#xff09; 了解设计模式 设计模式通俗的讲&#xff0c;就是一种更好的编写代码方案&#xff0c;打个比喻&#xff1a;从上海到武汉&#xff0c;你可以选择做飞机&#xff0c;做轮船&#xff0c;开车&#xff0c;骑摩托车多种方式&#xff0c;把出行…

【Kubernetes】四层代理Service

Service四层代理 一、Service概念原理1.1、为什么要有Service1.2、Service概述1.3、工作原理1.4、三类IP地址【1】Node Network&#xff08;节点网络&#xff09;【2】Pod network&#xff08;pod 网络&#xff09;【3】Cluster Network&#xff08;服务网络&#xff09; 二、S…

四川技能大赛——2023年四川网信人才技能大赛(网络安全管理员赛项)决赛

四川技能大赛——2023年四川网信人才技能大赛&#xff08;网络安全管理员赛项&#xff09;决赛 文章目录 四川技能大赛——2023年四川网信人才技能大赛&#xff08;网络安全管理员赛项&#xff09;决赛C1-比64少的bas - DONEC2-affine - DONEC3-简单的RSA - DONEM1-不要动我的f…

【C++数据结构 | 字符串速通】10分钟秒杀字符串相关操作 | 字符串的增删改查 | 字符串与数组相互转换

字符串 by.Qin3Yu 文中所有代码默认已使用std命名空间且已导入部分头文件&#xff1a; #include <iostream> #include <string> using namespace std;概念速览 字符串是一种非常好理解的数据类型&#xff0c;它用于存储和操作文本数据。字符串可以包含任意字符…

认识存储管理

存储器是计算机系统中最重要的资源之一。因为任何程序和数据以及各种控制用的数据结构都必须占有一定的存储空间&#xff0c;因此&#xff0c;存储管理直接影响系统性能。 存储器由内存和外存组成。内存是由系统实际提供的存储单元&#xff08;常指字节&#xff09;组成的一个连…

delphi android打开外部文件,报错android.os.FileUriExposedException解决方法

Android 7.0强制启用了被称作 StrictMode的策略&#xff0c;带来的影响就是你的App对外无法暴露file://类型的URI了。 如果你使用Intent携带这样的URI去打开外部App(比如&#xff1a;打开系统相机拍照)&#xff0c;那么会抛出FileUriExposedException异常。 Delphi 为Android…

spring集成mybatis简单教程

首先说下实现了什么效果&#xff0c;就是不用每次查询前手动创建 sessionFactory和添加datasource文件了。 整个工程结构是这样的 这次我也把代码放在了gitee上&#xff0c;方便大家更全貌的看到所有的实现细节。代码链接如下&#xff1a; Java: 一些Java代码 (gitee.com) …

交换机基本原理和配置

目录 一、数据链路层功能 二、交换机的工作原理 三、交换机的四大功能 一、数据链路层功能 位于网络层与物理层之间 数据链路的建立、维护与拆除帧包装、帧传输、帧同步帧的差错恢复流量控制 二、交换机的工作原理 交换机通过数据帧的源 MAC 地址&#xff0c;学习到交换机端…

生成模型 | 数字人类的三维重建(3D reconstruction)调研及总结【20231210更新版】

本文主要集中于图片到三维重建的算法模型&#xff0c;其中包含人体重建&#xff0c;人脸重建等 1.三维人体重建 1.1.2015_SMPL: A Skinned Multi-Person Linear Model 论文地址&#xff1a;SMPL2015.pdf (mpg.de) 代码地址&#xff1a;CalciferZh/SMPL: NumPy, TensorFlow an…

我的隐私计算学习——隐私集合求交(1)

笔记内容来自多本书籍、学术资料、白皮书及ChatGPT等工具&#xff0c;经由自己阅读后整理而成。 &#xff08;一&#xff09;PSI的介绍 隐私计算关键技术&#xff1a;隐私集合求交&#xff08;PSI&#xff09;原理介绍 隐私计算关键技术&#xff1a;隐私集合求交&#xff08…

利用Node.js和cpolar实现远程访问,无需公网IP和路由器设置的完美解决方案

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

Python开发运维:Python垃圾回收机制

目录 一、理论 1.Python垃圾回收机制 一、理论 1.Python垃圾回收机制 &#xff08;1&#xff09;引⽤计数器 1&#xff09;环状双向链表 refchain 在python程序中创建的任何对象都会放在refchain链表中。 name "david" age 20 hobby ["篮球",游泳…

【链表Linked List】力扣-114 二叉树展开为链表

目录 题目描述 解题过程 官方题解 题目描述 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。展开后的单链表应…

渗透测试——十、渗透列举及命令详解

渗透测试 一、协议配置与分析1、HTTPS 的定义2、HTTPS的验证 二、Kali Linux 常用工具三、Windows 命令详解 一、协议配置与分析 1、HTTPS 的定义 HTTPS (Hyper Text Transfer Protocol over Secure Socket Layer&#xff0c;超文本传输安全协议)是以安全为目标的 HTTP 通道。…

IDEA Maven 配置国内源

基本步骤 分别设置下图的两个&#xff0c;一个是对当前项目的设置&#xff0c;一个是对以后创建的项目设置&#xff0c;这样以后就不用重新配置了。 将下面的两个勾选上 注意&#xff0c;两个地方&#xff0c;Settings 和 Settings for New Projects 的勾都要勾上。 前往 User…

【C语言基础】嵌入式面试经典题(C语言篇)----有新的内容会及时补充、更新!

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…