LCP 74. 最强祝福力场
题目
差分数组(Difference Array)
差分数组是一种用于高效处理对原数组某个区间内所有元素进行同一操作(如增加或减少同一个值)的技术。在二维情况下,差分数组可以用于记录二维区域内部的变化,如本例中的力场强度变化。通过在差分数组的特定位置加上或减去值,可以实现对原数组(即实际的力场分布图)的快速更新。具体操作如下:
-
增加操作:对于一个左上角为
(a, b)
,右下角为(c, d)
的矩形区域,其在差分数组diff
中的操作为:diff[a][b]++
,diff[a][d+1]--
,diff[c+1][b]--
,diff[c+1][d+1]++
。这些操作保证了当差分数组还原到原数组时,只有(a, b)
到(c, d)
区域内的值增加了。 -
计算原数组值:通过对差分数组进行前缀和操作(累加从起点到当前点的所有变化量),可以还原出每个位置的实际值。
fieldOfGreatestBlessing(int[][] forceField)
这是主方法,用于计算并返回力场强度最大区域的最大值。
-
优先队列和哈希图初始化:创建两个优先队列
px
和py
,分别用于存储力场中心点 X 坐标和 Y 坐标的变化值,以及两个哈希图mapX
和mapY
用于记录坐标到索引的映射。 -
处理力场中心点:遍历每个力场,对于每个力场的 X 和 Y 坐标,分别计算它们加上半径和减去半径的值(左移操作用于防止坐标溢出),并通过
addX
和addY
方法更新mapX
、mapY
、px
、py
。 -
建立坐标到索引的映射:将
px
和py
中的元素分别轮询,并使用递增的索引值更新mapX
和mapY
,为接下来的差分数组计算做准备。 -
初始化差分数组:根据
mapX
和mapY
的大小初始化一个差分数组diff
,这个数组用于记录每个坐标点的力场变化量。 -
更新差分数组:遍历每个力场,调用
addRegion
方法更新差分数组,记录力场覆盖区域内每一点的力场增加值。 -
计算最大力场值:调用
build
方法处理差分数组,计算每个点的实际力场值,并找到最大的力场值作为结果返回。
addRegion(int[][] diff, int a, int b, int c, int d)
这个方法用于在差分数组 diff
中记录一个力场覆盖区域。通过增加左上角和减少右下角的对角线之外的区域,以达到增加内部区域的目的。
addX
和 addY
这两个方法负责更新 mapX
、mapY
、px
、py
,为新的坐标值创建映射并加入到优先队列中,如果这个坐标值之前没有被处理过的话。
build(int[][] diff)
这个方法处理差分数组 diff
,通过累加左上角到当前点的变化量,计算出每个点的实际力场值,并找到并返回最大的力场值。
代码
class Solution {
// 计算力场强度最大区域的最大力场值
public int fieldOfGreatestBlessing(int[][] forceField) {
// 用于存储X坐标变化点,按自然顺序排列
PriorityQueue<Long> px = new PriorityQueue<>();
// 用于存储Y坐标变化点,按逆自然顺序排列
PriorityQueue<Long> py = new PriorityQueue<>((a, b) -> b.compareTo(a));
// 存储X坐标到索引的映射
HashMap<Long, Integer> mapX = new HashMap<>();
// 存储Y坐标到索引的映射
HashMap<Long, Integer> mapY = new HashMap<>();
// 遍历每个力场
for (int[] f : forceField) {
long r = f[2];
// 处理X坐标
addX(mapX, px, ((long)f[0] << 1) + r);
addX(mapX, px, ((long)f[0] << 1) - r);
// 处理Y坐标
addY(mapY, py, ((long)f[1] << 1) + r);
addY(mapY, py, ((long)f[1] << 1) - r);
}
// 建立X坐标到索引的映射
int index = 1;
while (!px.isEmpty()) {
mapX.put(px.poll(), index++);
}
// 建立Y坐标到索引的映射
index = 1;
while (!py.isEmpty()) {
mapY.put(py.poll(), index++);
}
// 初始化差分数组
int sizeX = mapX.size();
int sizeY = mapY.size();
int[][] diff = new int[sizeY + 2][sizeX + 2];
// 更新差分数组
for (int[] f : forceField) {
long r = f[2];
addRegion(diff, mapY.get((long)((f[1] << 1) + r)), mapX.get((long)((f[0] << 1) - r)), mapY.get((long)((f[1] << 1) - r)), mapX.get((long)((f[0] << 1) + r)));
}
// 计算最大力场值
return build(diff);
}
// 更新差分数组以记录力场影响区域
public void addRegion(int[][] diff, int a, int b, int c, int d) {
diff[a][b]++;
diff[a][d + 1]--;
diff[c + 1][b]--;
diff[c + 1][d + 1]++;
}
// 添加X坐标变化点
public void addX(HashMap<Long, Integer> mapX, PriorityQueue<Long> px, Long x) {
if (!mapX.containsKey(x)) {
mapX.put(x, -1);
px.add(x);
}
}
// 添加Y坐标变化点
public void addY(HashMap<Long, Integer> mapY, PriorityQueue<Long> py, Long y) {
if (!mapY.containsKey(y)) {
mapY.put(y, -1);
py.add(y);
}
}
// 通过处理差分数组来计算并返回力场最大值
public int build(int[][] diff) {
int ans = 0;
// 累加前缀和来还原实际力场值
for (int i = 1; i < diff.length; i++) {
for (int j = 1; j < diff[0].length; j++) {
diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1];
ans = Math.max(ans, diff[i][j]); // 更新最大力场值
}
}
return ans;
}
}