题意理解
若目标位于爆破正方形的边上,该目标不会被摧毁
如图,若炸弹刚好在第一张图上的位置,则只能炸掉中间四个目标(蓝色的表示目标)
若炸弹偏移一点,若在下图位置,则可以炸掉九个目标
由此我们可以知道,炸弹范围为R的炸弹最多可以炸掉 R*R个目标
基本思路
由于要求RR范围目标价值的最大值,我们的做法是枚举这个RR矩阵的右下标,使用二维前缀和进行计算,由此得到最大值。
细节问题
1. 地图范围的取值
(1)由于题目中并没有明确告诉我们地图有多大,我们需要由题中给出的目标的坐标x的最大值和y的最大值得到地图的范围n,m
(2)炸弹范围的矩阵中,右下角的坐标若为(x,y)的话,由于炸弹矩阵的边长是R,右下角已经是第一个格子的话,再往上数R个格子,则左上角的坐标应该是(x-R+1,y-R+1)
(3) 若n或者m小于给定的炸弹范围R的话,由于循环是从R开始枚举的,因此我们有可能无法枚举矩阵,因此我们需要保证n和m的最小值是R
(4)不同的目标可能出现在同一个位置,因此应该是s[a+1][b+1]+=w而不是直接等于w
(5) 计算前缀和,下标一般从1开始;本题中只能开一个s数组,开两个会爆内存,因此我们可以只使用一个数组s,记录值的同时计算前缀和数组
但是边长为x的话,会有x+1个目标,由图中可以看出,因此要把他们全部炸掉,R+1范围的炸弹即可,题目中x的最大值是5000,而R的取值为1e9,由于x最大值是5000,因此5001的范围即可,多了也是同样的效果。
代码如下
import java.util.*;
public class Main
{
static int N=5010;
static int[][] s=new int[N][N];
static int n,r,m,cnt;
static int res;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
cnt=sc.nextInt();
r=sc.nextInt();
// 题目中给出的r比较大 但实际上 经过我们画图得知 r的炸弹范围可以炸r个点 而边长为x 的话 实际上是有x+1个点 因此需要r+1的炸弹
// 也就是说 要想炸掉题目中范围 的点 最少需要x+1 的炸弹 多了的话也没用,可能还会少枚举矩阵 因此当r大于x+1的时候 我们就需要让r取r+1
r=Math.min(r,5001);
n = m = r;
for(int i=1;i<=cnt;i++) {
int a=sc.nextInt();
int b=sc.nextInt();
// 计算出这个地图的长和宽
n=Math.max(a+1, n);
m=Math.max(b+1, m);
int w=sc.nextInt();
s[a+1][b+1]+=w;
}
// 计算前缀和
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+s[i][j];
}
}
//for(int i=1;i<=n;i++) {
//
// for(int j=1;j<=n;j++) {
//
// System.out.print(s[i][j]+" ");
//
// }
// System.out.println();
//
//
// }
// 摆放炸弹
// 枚举炸弹位置
for(int i=r;i<=n;i++) {
for(int j=r;j<=m;j++) {
res = Math.max(res, s[i][j] - s[i - r][j] - s[i][j - r] + s[i - r][j - r]);
}
}
System.out.println(res);
}
}