❗❗❗必看:
下列题我全部都使用 Java 语言写的,并且均可以提交成功,获得Accepted 结果的. 如果代码和详解看了之后,对答案有任何疑问,都可以在评论区提出来,我都会一个一个回答.
❗❗❗感谢大家的支持,如果喜欢我的博客,关注 点赞 收藏 评论一波,非常感谢!!!
题目:垃圾炸弹
2018年俄罗斯世界杯(2018 FIFA World Cup)开踢啦!为了方便球迷观看比赛,莫斯科街道上很多路口都放置了的直播大屏幕,但是人群散去后总会在这些路口留下一堆垃圾。为此俄罗斯政府决定动用一种最新发明——“垃圾炸弹”。这种“炸弹”利用最先进的量子物理技术,爆炸后产生的冲击波可以完全清除波及范围内的所有垃圾,并且不会产生任何其他不良影响。炸弹爆炸后冲击波是以正方形方式扩散的,炸弹威力(扩散距离)以d给出,表示可以传播d条街道。
例如下图是一个d=1的“垃圾炸弹”爆炸后的波及范围。
假设莫斯科的布局为严格的1025*1025的网格状,由于财政问题市政府只买得起一枚“垃圾炸弹”,希望你帮他们找到合适的投放地点,使得一次清除的垃圾总量最多(假设垃圾数量可以用一个非负整数表示,并且除设置大屏幕的路口以外的地点没有垃圾)。
Input
第一行给出“炸弹”威力d(1 <= d <= 50)。第二行给出一个数组n(1 <= n <= 20)表示设置了大屏幕(有垃圾)的路口数目。接下来n行每行给出三个数字x, y, i, 分别代表路口的坐标(x, y)以及垃圾数量i. 点坐标(x, y)保证是有效的(区间在0到1024之间),同一坐标只会给出一次。
Output
输出能清理垃圾最多的投放点数目,以及能够清除的垃圾总量。
测试样例
输入
1
2
4 4 10
6 6 20
输出
1 30
代码
//垃圾炸弹
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int d = scanner.nextInt(); // 炸弹威力(扩散距离)
int n = scanner.nextInt(); // 有垃圾的路口数量
int[][] grid = new int[1025][1025]; // 网格
// 读取每个垃圾点的坐标和垃圾数量,并填充到网格中
for (int i = 0; i < n; i++) {
int x = scanner.nextInt(); // 横坐标
int y = scanner.nextInt(); // 纵坐标
int garbage = scanner.nextInt(); // 垃圾数量
grid[x][y] = garbage;
}
// 调用计算最大清理垃圾数量的方法
int[] result = getMaxGarbageCleared(d, grid);
System.out.println(result[0] + " " + result[1]);
scanner.close();
}
public static int[] getMaxGarbageCleared(int d, int[][] grid) {
int gridSize = 1025; // 网格大小
int[][] prefixSum = new int[gridSize][gridSize];
// 构建二维前缀和数组
for (int i = 0; i < gridSize; i++) {
for (int j = 0; j < gridSize; j++) {
prefixSum[i][j] = grid[i][j];
if (i > 0) prefixSum[i][j] += prefixSum[i - 1][j];
if (j > 0) prefixSum[i][j] += prefixSum[i][j - 1];
if (i > 0 && j > 0) prefixSum[i][j] -= prefixSum[i - 1][j - 1];
}
}
int maxGarbage = 0; // 清理的最大垃圾数量
int bestPoints = 0; // 最佳投放点数量
// 遍历所有可能的投放点
for (int x = 0; x < gridSize; x++) {
for (int y = 0; y < gridSize; y++) {
// 计算炸弹爆炸覆盖的区域的边界
int x1 = Math.max(0, x - d);
int y1 = Math.max(0, y - d);
int x2 = Math.min(gridSize - 1, x + d);
int y2 = Math.min(gridSize - 1, y + d);
// 计算覆盖区域的垃圾总量
int totalGarbage = prefixSum[x2][y2];
if (x1 > 0) totalGarbage -= prefixSum[x1 - 1][y2];
if (y1 > 0) totalGarbage -= prefixSum[x2][y1 - 1];
if (x1 > 0 && y1 > 0) totalGarbage += prefixSum[x1 - 1][y1 - 1];
// 更新最大垃圾量和最佳投放点计数
if (totalGarbage > maxGarbage) {
maxGarbage = totalGarbage;
bestPoints = 1;
} else if (totalGarbage == maxGarbage) {
bestPoints += 1;
}
}
}
return new int[]{bestPoints, maxGarbage};
}
}
详解
初步思路
利用二维前缀和技术快速计算炸弹爆炸后影响范围内的垃圾总量,通过遍历所有可能的投放点,找出使得垃圾清理量最大的点,并统计这样的点的数量。
具体步骤
输入解析
- 读取炸弹威力d,垃圾点数量n。
- 初始化一个大小为1025x1025的二维网格grid,用于记录每个点的垃圾数量。
- 读取每个垃圾点的坐标和垃圾数量,填入网格grid中。
构建二维前缀和数组
- 构建一个前缀和数组prefixSum,用于快速计算任意矩形区域内的垃圾总量。
- 计算前缀和数组,每个位置prefixSum[i][j]表示从(0,0)到(i,j)的矩形区域内的垃圾总量。
遍历所有可能的投放点
- 对于网格中的每个点(x, y),计算炸弹爆炸范围的矩形区域(边界为[x-d, x+d]和[y-d, y+d],并确保边界在网格范围内)。
- 利用前缀和数组计算该区域内的垃圾总量。
- 记录能够清理的最大垃圾总量,并统计能够达到该总量的最佳投放点的数量。
输出结果
输出能够清理最大垃圾总量的投放点数量以及最大清理量。
总结方法
这种方法充分利用了前缀和技术来快速计算任意矩形区域的垃圾总量,大大减少了计算量,提升了效率。前缀和数组的构建和使用,是解决二维区域和相关问题的有效工具。通过这种方式,可以在处理大量数据时,快速获取结果,提高程序的性能。