2023 年力扣杯春季编程大赛题解
题目集合:
- 2 分 - 补给马车
- 4 分 - 探险营地
- 6 分 - 最强祝福力场
- 8 分 - 传送卷轴
- 10 分 - 魔法棋盘
# T1. 补给马车
# 思路
模拟
# 代码
class Solution { | |
public int[] supplyWagon(int[] supplies) { | |
int n = supplies.length; | |
int newLen = n / 2; | |
for (int i = n; i > newLen; --i) { | |
int k = 0; | |
int mv = Integer.MAX_VALUE; | |
for (int j = 0; j < i - 1; ++j) { | |
if (supplies[j] + supplies[j + 1] < mv) { | |
mv = supplies[j] + supplies[j + 1]; | |
k = j; | |
} | |
} | |
supplies[k] += supplies[k + 1]; | |
for (int j = k + 1; j < i - 1; ++j) { | |
supplies[j] = supplies[j + 1]; | |
} | |
} | |
int[] ans = new int[newLen]; | |
System.arraycopy(supplies, 0, ans, 0, newLen); | |
return ans; | |
} | |
} |
# T2. 探险营地
# 思路
模拟
# 代码
class Solution { | |
public int adventureCamp(String[] es) { | |
Set<String> old = new HashSet<>(); | |
String[] ss = es[0].split("->"); | |
for (String s : ss) old.add(s); | |
int cnt = 0; | |
int idx = -1; | |
for (int i = 1; i < es.length; ++i) { | |
ss = es[i].split("->"); | |
int c = 0; | |
for (String s : ss) { | |
if (s.length() > 0 && !old.contains(s)) { | |
old.add(s); | |
++c; | |
} | |
} | |
if (c > cnt) { | |
cnt = c; | |
idx = i; | |
} | |
} | |
return idx; | |
} | |
} |
# T3. 最强祝福力场
# 解题思路
- 坐标离散化
- 坐标点整数化
- 建图进行二维差分(矩阵)
- 求矩阵的前缀和
具体看代码注释
# 代码
class Solution { | |
public int fieldOfGreatestBlessing(int[][] forceField) { | |
// 1. 坐标点离散化 | |
// 有序集合:唯一 + 有序 | |
Set<Long> xs = new TreeSet<>(); | |
Set<Long> ys = new TreeSet<>(); | |
for (int[] fo : forceField) { | |
// x - side / 2: | |
// 考虑到可能为小数,故将坐标转成整数:2 * x - side | |
// 左上角坐标 | |
long x = 2L * fo[0] - fo[2]; | |
long y = 2L * fo[1] - fo[2]; | |
xs.add(x); | |
ys.add(y); | |
// 右下角坐标 | |
x = 2L * fo[0] + fo[2]; | |
y = 2L * fo[1] + fo[2]; | |
xs.add(x); | |
ys.add(y); | |
} | |
int xn = xs.size(); | |
int yn = ys.size(); | |
long[] xAxis = new long[xn]; | |
long[] yAxis = new long[yn]; | |
int k = 0; | |
for (long x : xs) xAxis[k++] = x; | |
k = 0; | |
for (long y : ys) yAxis[k++] = y; | |
// 2. 二维差分 | |
int[][] m = new int[xn][yn]; | |
for (int[] f : forceField) { | |
long lx = 2L * f[0] - f[2]; | |
long rx = 2L * f[0] + f[2]; | |
long ty = 2L * f[1] - f[2]; | |
long by = 2L * f[1] + f[2]; | |
// 通过二分找到当前坐标的离散化后的下标 | |
int lxi = Arrays.binarySearch(xAxis, lx); | |
int rxi = Arrays.binarySearch(xAxis, rx); | |
int tyi = Arrays.binarySearch(yAxis, ty); | |
int byi = Arrays.binarySearch(yAxis, by); | |
if (lxi >= 0 && tyi >= 0 && rxi >= 0 && byi >= 0) { | |
// 左上 +1 | |
++m[lxi][tyi]; | |
// 左下 -1 | |
if (byi + 1 < yn) --m[lxi][byi + 1]; | |
// 右上 -1 | |
if (rxi + 1 < xn) --m[rxi + 1][tyi]; | |
// 右下 +1 | |
if (byi + 1 < yn && rxi + 1 < xn) ++m[rxi + 1][byi + 1]; | |
} | |
} | |
// 二维前缀和计算答案 | |
int max = 0; | |
for (int i = 0; i < xn; ++i) { | |
for (int j = 0; j < yn; ++j) { | |
if (j > 0) m[i][j] += m[i][j - 1]; | |
if (i > 0) m[i][j] += m[i - 1][j]; | |
if (i > 0 && j > 0) m[i][j] -= m[i - 1][j - 1]; | |
max = Math.max(m[i][j], max); | |
} | |
} | |
return max; | |
} | |
} |
# T4. 传送卷轴
# 解题思路
BFS + 二分枚举
# 代码
class Solution { | |
final int[] dx = new int[] {0, 0, -1, 1}; | |
final int[] dy = new int[] {1, -1, 0, 0}; | |
char[][] mat; | |
public int challengeOfTheKeeper(String[] maze) { | |
/** | |
* 1. 找到【初始位置】和【魔法水晶】位置 | |
* 2. bfs 计算【魔法水晶】位置到每个可达点的最短距离 dis [i][j] | |
* 3. 二分答案 | |
*/ | |
int n = maze.length; | |
mat = new char[n][]; | |
// 【初始位置】的坐标 | |
int startX = -1, startY = -1; | |
// 【魔法水晶】的位置坐标 | |
int endX = -1, endY = -1; | |
for (int i = 0; i < n; ++i) { | |
mat[i] = maze[i].toCharArray(); | |
if (startX + startY < 0 || endX + endY < 0) { | |
for (int j = 0; j < n; ++j) { | |
if (mat[i][j] == 'S') { | |
startX = j; | |
startY = i; | |
} else if (mat[i][j] == 'T') { | |
endX = j; | |
endY = i; | |
} | |
} | |
} | |
} | |
// 2. 计算【魔法水晶】到各个点的最短距离 | |
int[][] dis = new int[n][n]; | |
Queue<int[]> que = new ArrayDeque<>(); | |
for (int i = 0; i < n; ++i) Arrays.fill(dis[i], Integer.MAX_VALUE); | |
que.offer(new int[]{endX, endY}); | |
dis[endY][endX] = 0; | |
while (!que.isEmpty()) { | |
for (int i = que.size(); i > 0; --i) { | |
int[] cur = que.poll(); | |
int curX = cur[0]; | |
int curY = cur[1]; | |
for (int j = 0; j < dx.length; ++j) { | |
int nxtX = curX + dx[j]; | |
int nxtY = curY + dy[j]; | |
if (0 <= nxtX && nxtX < n && 0 <= nxtY && nxtY < n | |
// 不是墙壁 | |
&& mat[nxtY][nxtX] != '#' | |
// 还未访问过 | |
&& dis[nxtY][nxtX] == Integer.MAX_VALUE) { | |
que.offer(new int[] {nxtX, nxtY}); | |
dis[nxtY][nxtX] = dis[curY][curX] + 1; | |
} | |
} | |
} | |
} | |
// 初始位置本身就无法达到【魔法水晶】 | |
if (dis[startY][startX] == Integer.MAX_VALUE) return -1; | |
// 3. 二分枚举答案 | |
int l = -1, r = n * n + 1; | |
int [][] vis = new int[n][n]; | |
while (l + 1 < r) { | |
int step = (l + r) / 2; | |
if (dfs(startX, startY, step, vis, dis)) r = step; | |
else l = step; | |
} | |
return r > n * n ? -1 : r; | |
} | |
// 判断是否能在 maxStep 步数内能否到达【魔法水晶】 | |
private boolean dfs(int x, int y, int maxStep, int[][] vis, int[][] dis) { | |
int n = mat.length; | |
if (x < 0 || x >= n || y < 0 || y >= n // 越界 | |
|| mat[y][x] == '#' // 走到墙壁 | |
|| vis[y][x] == maxStep + 1) // 没有走过 | |
return false; | |
// 到达终点 | |
if (mat[y][x] == 'T') return true; | |
vis[y][x] = maxStep + 1; | |
// 守护者使用卷轴,如果无法在 maxDis 内到达,则返回 false | |
if (mat[y][x] == '.' | |
&& (mat[y][n - 1 - x] != '#' && dis[y][n - 1 - x] > maxStep | |
|| mat[n - 1 - y][x] != '#' && dis[n - 1 - y][x] > maxStep)) | |
return false; | |
for (int i = 0; i < dx.length; ++i) { | |
if (dfs(x + dx[i], y + dy[i], maxStep, vis, dis)) | |
return true; | |
} | |
return false; | |
} | |
} |