1 solutions
-
0
题面描述
塔子哥有一些神奇的镜子,能够吸收光芒并在一定时间后散射。镜子分为一级镜和二级镜,一级镜散射光芒需1毫秒,而二级镜需2毫秒。塔子哥将这些镜子放在一个二维矩阵中,并给定光源镜子的坐标。输入包括矩阵的行列数、光源坐标以及每个位置的镜子等级(0为墙体,1为一级镜,2为二级镜)。输出为所有镜子最早吸收到光芒的时间,若有镜子无法吸收到光芒则输出-1。
思路:Dijkstra
目标是找到所有镜子接收到光芒的最早时间。因此可以想到单源最短路算法:dijkstra
首先,我们需要创建一个二维数组
dis
来存储每个镜子接收到光芒的时间,初始值设为一个较大的数。同时,我们需要一个二维数组bk
来标记每个镜子是否已经接收到光芒,初始值设为False
。然后,我们使用优先队列
q
,并将初始镜子的坐标和接收到光芒的时间(设为 0)加入队列。接下来,我们进行Dijkstra算法。在每一步,我们取出队列中时间最早的镜子,然后更新其四个方向的镜子的接收时间。如果新的接收时间比当前的接收时间早,我们就更新接收时间,并将新的镜子加入队列。
最后,我们遍历所有的镜子,找出接收到光芒的最晚时间,即为所有镜子都能够吸收到光芒的最早时间。
算法步骤:
-
初始化:
- 创建一个二维数组
dist
来存储每个镜子接收到光芒的时间,初始值设为一个较大的数(INT_MAX / 2
),以避免溢出。 - 创建一个二维布尔数组
visited
来标记每个镜子是否已经接收到光芒,初始值设为false
。 - 使用优先队列
pq
存储每个镜子的坐标和接收到光芒的时间,并将初始镜子的坐标和时间(0)加入队列。
- 创建一个二维数组
-
Dijkstra算法:
- 在每一步中,从队列中取出时间最早的镜子,检查其四个方向(上下左右)的邻接镜子。
- 如果邻接镜子的接收时间比当前的接收时间早,则更新接收时间,并将邻接镜子加入队列。
- 继续这个过程,直到所有可达的镜子都接收到光芒或队列为空。
-
结果计算:
- 遍历所有镜子,找出接收到光芒的最晚时间,即为所有镜子都能够吸收到光芒的最早时间。如果有镜子仍未接收到光芒,则返回-1。
时间复杂度
代码
C++
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> pii; int main() { vector<int> directions{-1, 0, 1, 0, -1}; // 四个方向的移动向量 int n, m; cin >> n; // 矩阵的列数 cin >> m; // 矩阵的行数 int si, sj; cin >> si >> sj; // 初始光源镜子的坐标 int left = 0; // 镜子的数量 vector<vector<int>> grid(n, vector<int>(m, 0)); // 存储镜子的等级 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> grid[i][j]; // 输入每个位置的镜子等级 if (grid[i][j] > 0) left++; // 统计可接收光芒的镜子数量 } } vector<vector<int>> dist(n, vector<int>(m, INT_MAX / 2)); // dist数组,用于存储到达每个镜子的时间 vector<vector<bool>> visited(n, vector<bool>(m, false)); // visited数组,标记镜子是否接收到光芒 priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq; // 优先队列,存储<时间, 镜子坐标> pq.push({0, si, sj}); // 将初始镜子加入队列 dist[si][sj] = 0; // 初始镜子的接收时间为0 left--; // 初始镜子已接收到光芒 while (!pq.empty()) { auto item = pq.top(); // 取出时间最早的镜子 int d = item[0], i = item[1], j = item[2]; pq.pop(); if (visited[i][j]) continue; // 如果该镜子已接收到光芒,跳过 visited[i][j] = true; // 标记该镜子已接收到光芒 // 检查四个方向 for (int k = 0; k < 4; k++) { int nx = i + directions[k]; // 新的x坐标 int ny = j + directions[k + 1]; // 新的y坐标 // 判断新坐标是否在有效范围内,且为镜子 if (nx >= 0 && nx < n && ny >= 0 && ny < m && grid[nx][ny] > 0 && dist[nx][ny] > d + grid[i][j] && !visited[nx][ny]) { if (dist[nx][ny] >= INT_MAX / 2) // 刚到达了一个镜子 left--; // 剩余可接收光芒的镜子数量减一 dist[nx][ny] = d + grid[i][j]; // 更新接收时间 pq.push({dist[nx][ny], nx, ny}); // 将新镜子加入队列 } } if (left == 0) break; // 如果所有镜子都接收到光芒,结束 } int stamp = 0; // 存储接收到光芒的最晚时间 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (grid[i][j] > 0) // 只检查镜子 stamp = max(stamp, dist[i][j]); // 更新最晚时间 } } if (stamp >= INT_MAX / 2) { stamp = -1; // 如果未能接收到光芒,返回-1 } cout << stamp << endl; // 输出结果 return 0; }
python代码
import heapq # 导入堆队列算法库,用于实现优先队列 # 输入矩阵的行数和列数 m = int(input()) # 行数 n = int(input()) # 列数 # 输入光源镜子的起始坐标 s, e = map(int, input().split()) # 输入矩阵数据,表示镜子的等级 a = [list(map(int, input().split())) for i in range(n)] # 初始化距离数组,设为一个很大的数(模拟无穷大) dis = [[10**9] * m for i in range(n)] # 初始化访问标记数组,标记镜子是否已经接收到光芒 bk = [[False] * m for i in range(n)] # 定义四个方向的移动向量(上下左右) dx = [1, -1, 0, 0] dy = [0, 0, 1, -1] # 设置初始光源的接收时间为0,并将其加入优先队列 dis[s][e] = 0 q = [] heapq.heappush(q, [0, s, e]) # 将初始镜子的坐标和时间加入队列 # Dijkstra算法的主循环 while len(q) > 0: # 从队列中取出时间最早的镜子 u = q.pop(0) # 弹出优先队列中的最小元素 u = u[1:] # 获取镜子的坐标 if bk[u[0]][u[1]]: # 如果该镜子已接收到光芒,跳过 continue bk[u[0]][u[1]] = True # 标记该镜子已接收到光芒 # 遍历四个方向的邻接镜子 for d in range(4): x = u[0] + dx[d] # 新的x坐标 y = u[1] + dy[d] # 新的y坐标 # 检查新坐标是否在有效范围内,且为镜子 if x < 0 or x >= n or y < 0 or y >= m or a[x][y] == 0: continue # 跳过不合法的坐标或墙体 # 更新接收时间 if dis[x][y] > dis[u[0]][u[1]] + a[u[0]][u[1]]: dis[x][y] = dis[u[0]][u[1]] + a[u[0]][u[1]] # 更新新镜子的接收时间 heapq.heappush(q, [dis[x][y], x, y]) # 将新镜子加入队列 # 计算所有镜子接收到光芒的最晚时间 ans = 0 # 最晚接收到光芒的时间 ok = True # 用于判断是否所有镜子都接收到光芒 # 遍历所有镜子,找出接收到光芒的最晚时间 for i in range(n): for j in range(m): if a[i][j] != 0: # 只检查镜子 ans = max(ans, dis[i][j]) # 更新最晚时间 # 输出结果,如果未能接收到光芒,返回-1 print(ans if ans != 10**9 else -1)
Java代码
import java.util.*; public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); // 创建输入扫描器 while (in.hasNextInt()) { // 当输入有整数时继续循环 int n = in.nextInt(); // 读取矩阵的行数 int m = in.nextInt(); // 读取矩阵的列数 int[][] mirrors = new int[n][m]; // 创建二维数组存储镜子的等级 int initI = in.nextInt(); // 读取光源镜子的起始行坐标 int initJ = in.nextInt(); // 读取光源镜子的起始列坐标 // 输入矩阵数据,填充镜子的等级 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { mirrors[i][j] = in.nextInt(); // 读取每个镜子的等级 } } // 初始化接收时间数组,设为一个很大的数(模拟无穷大) int[][] times = new int[n][m]; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { times[i][j] = Integer.MAX_VALUE; // 设置为无穷大 } } // 使用优先队列来实现Dijkstra算法 Queue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() { @Override public int compare(int[] o1, int[] o2) { return o1[2] - o2[2]; // 根据时间进行比较 } }); // 队列存储[x, y, 耗时] int[][] visited = new int[n][m]; // 访问标记数组 queue.offer(new int[]{initI, initJ, 0}); // 将起始点加入队列 // Dijkstra算法主循环 while (!queue.isEmpty()) { int[] poll = queue.poll(); // 从队列中取出耗时最小的元素 int x = poll[0]; // 当前镜子的行坐标 int y = poll[1]; // 当前镜子的列坐标 int time = poll[2]; // 当前镜子的接收时间 // 检查坐标是否在有效范围内 if (x < 0 || x >= mirrors.length || y < 0 || y >= mirrors[0].length) { continue; // 如果超出范围,跳过 } if (mirrors[x][y] == 0) { // 如果是墙体,跳过 continue; } if (visited[x][y] == 1) { // 如果该镜子已访问,跳过 continue; } visited[x][y] = 1; // 标记该镜子已访问 times[x][y] = time; // 记录该镜子接收光芒的时间 // 将四个方向的邻接镜子加入队列,计算新的接收时间 queue.offer(new int[]{x - 1, y, time + mirrors[x][y]}); // 上 queue.offer(new int[]{x + 1, y, time + mirrors[x][y]}); // 下 queue.offer(new int[]{x, y - 1, time + mirrors[x][y]}); // 左 queue.offer(new int[]{x, y + 1, time + mirrors[x][y]}); // 右 } // 计算所有镜子接收到光芒的最晚时间 int max = 0; // 存储最晚接收到光芒的时间 for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (mirrors[i][j] != 0) { // 只检查镜子 max = Math.max(max, times[i][j]); // 更新最晚接收时间 } } } // 输出结果,如果未能接收到光芒,则返回-1 if (max == Integer.MAX_VALUE) { System.out.println(-1); // 如果没有镜子接收到光芒,输出-1 } else { System.out.println(max); // 输出所有镜子接收到光芒的最晚时间 } } } }
-
- 1
Information
- ID
- 47
- Time
- 1000ms
- Memory
- 256MiB
- Difficulty
- 6
- Tags
- # Submissions
- 66
- Accepted
- 12
- Uploaded By