import java.io.*;/** 题目分析:一个最大5000 * 5000 的矩阵, 爆炸范围在 [0,10e9]* 地图上的目标是随机分布,如果要暴力计算每一个区间R的权值,会很麻烦* 可以用二维前缀和先将权值存起来* for(int i = 1;i <= n;i ++) {for(int j = 1;j <= m;j ++) {g[i][j] = g[i][j-1] + g[i-1][j] - g[i-1][j-1] + read[j-1];}}//前缀和下标有出现-1,所以下标从1开始算。真正的有效下标应该是[1,5001]再遍历其中的每个区间R * R取一个最大值其中(n,m) 表示距离远点最远的那个点(只遍历有目标的位置)for(int i = R;i <= n; i++)for(int j = R;j <= m; j++)res = Math.max(res,s[i][j] - s[i - R + 1 - 1][j] - s[i][j-R+1-1] + s[i-R+1-1][j-R+1-1]);所以读入位置的时候,除了要更新该位置的权值w,还要不断更新n,m.用来后序遍历当 R < 5001,可以用上面的式子算,如果 R >= 5001,就把R设置成 5001,否则进不去循环 如果把R设置成5001,一般n,m也到不了那么大,所以初始化n = m = R (防止出现爆炸范围大于所以目标位置)半径为R的爆炸范围可以摧毁 (R + 1)(R + 1)个点 但因为爆炸范围要与边平行,所以少炸一个因为只有范围 R * R能炸到,所以爆炸点上下、左右的跨度只有 R - 1;在算前缀和的时候, x1 = x2 - (R - 1) - 1 == x2 - R */
public class Main {static int n,m; //其中(n,m) 表示距离远点最远的那个点static final int N = 5010;static int[][] s = new int[N][N]; //用于存放前缀和//输入较多,用BufferedReaderstatic BufferedReader in = new BufferedReader(new InputStreamReader(System.in));public static void main(String[] args) throws IOException {int cnt,R; //表示目标个数和爆炸范围String[] init = in.readLine().split(" ");cnt = Integer.parseInt(init[0]);//R最多取到地图边界R = Math.min(5001, Integer.parseInt(init[1]));//因为要从R ~ n,与R ~ m遍历所有目标,所以如果 n < R 或者 m < R 会遍历不到//所以先让n = m = Rn = m = R;while(cnt -- > 0) {//init可以复用init = in.readLine().split(" ");int x = Integer.parseInt(init[0]);int y = Integer.parseInt(init[1]);int w = Integer.parseInt(init[2]);//计算前缀和下标从1开始,题目是从0开始,所有把题目的所有下标都++x++;y++;n = Math.max(n,x);m = Math.max(m,y);s[x][y] += 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];}}//遍历半径为R的正方形int res = 0;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);in.close();}}
前缀和例题:一维版前缀和
二维前缀和