算法刷题AcWing 99. 激光炸弹——前缀和

Posted Ricky_0528

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法刷题AcWing 99. 激光炸弹——前缀和相关的知识,希望对你有一定的参考价值。

地图上有 N N N 个目标,用整数 X i , Y i X_i,Y_i Xi,Yi 表示目标在地图上的位置,每个目标都有一个价值 W i W_i Wi

注意:不同目标可能在同一位置。

现在有一种新型的激光炸弹,可以摧毁一个包含 R × R R \\times R R×R 个位置的正方形内的所有目标。

激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆炸范围,即那个正方形的边必须和 x , y x,y x,y 轴平行。

求一颗炸弹最多能炸掉地图上总价值为多少的目标。

输入格式
第一行输入正整数 N N N R R R,分别代表地图上的目标数目和正方形的边长,数据用空格隔开。

接下来 N N N 行,每行输入一组数据,每组数据包括三个整数 X i , Y i , W i X_i,Y_i,W_i Xi,Yi,Wi,分别代表目标的 x x x 坐标, y y y 坐标和价值,数据用空格隔开。

输出格式
输出一个正整数,代表一颗炸弹最多能炸掉地图上目标的总价值数目。

数据范围
0 ≤ R ≤ 109 0 \\leq R \\leq 109 0R109
0 ≤ N ≤ 10000 0 \\leq N \\leq 10000 0N10000
0 ≤ X i , Y i ≤ 5000 0 \\leq X_i,Y_i \\leq 5000 0Xi,Yi5000
0 ≤ W i ≤ 1000 0 \\leq W_i \\leq 1000 0Wi1000
输入样例

2 1
0 0 1
1 1 1

输出样例

1

①分析

首先矩形的边不一定是在整点上,故不能去枚举矩形的位置,情况太多了,因此需要换一种枚举方式 可以去枚举它一次能框出来的矩形矩阵,对于一个 R × R 的矩形,一次最多可以框入 R × R 个点 可以将点转化为一个一个的小方块,每次看矩形框入的小方块有多少个 矩形的边不能与小方块的边重合,这样的话边上的点都不能被炸到,结果不可能大,如下图所示 即每次可以摧毁一个 ( R − 1 ) × ( R − 1 ) 的矩阵 因此只需要暴力枚举一下所有的 ( R − 1 ) × ( R − 1 ) 上点的和最后求一个最大值即可 要快速的求和可以用二位前缀和 题目中 R ≤ 1 0 9 ,但是 X , Y ≤ 5000 ,故 R ≥ 5001 就可以把整块区域全都摧毁,因此太大无意义 首先矩形的边不一定是在整点上,故不能去枚举矩形的位置,情况太多了,因此需要换一种枚举方式\\\\ 可以去枚举它一次能框出来的矩形矩阵,对于一个R \\times R的矩形,一次最多可以框入R \\times R个点\\\\ 可以将点转化为一个一个的小方块,每次看矩形框入的小方块有多少个\\\\ 矩形的边不能与小方块的边重合,这样的话边上的点都不能被炸到,结果不可能大,如下图所示\\\\ 即每次可以摧毁一个(R-1)\\times(R-1)的矩阵\\\\ 因此只需要暴力枚举一下所有的(R-1)\\times(R-1)上点的和最后求一个最大值即可\\\\ 要快速的求和可以用二位前缀和\\\\ 题目中R \\leq10^9,但是X,Y\\leq 5000,故R\\geq5001就可以把整块区域全都摧毁,因此太大无意义 首先矩形的边不一定是在整点上,故不能去枚举矩形的位置,情况太多了,因此需要换一种枚举方式可以去枚举它一次能框出来的矩形矩阵,对于一个R×R的矩形,一次最多可以框入R×R个点可以将点转化为一个一个的小方块,每次看矩形框入的小方块有多少个矩形的边不能与小方块的边重合,这样的话边上的点都不能被炸到,结果不可能大,如下图所示即每次可以摧毁一个(R1)×(R1)的矩阵因此只需要暴力枚举一下所有的(R1)×(R1)上点的和最后求一个最大值即可要快速的求和可以用二位前缀和题目中R109,但是X,Y5000,故R5001就可以把整块区域全都摧毁,因此太大无意义

②代码

#include <cstdio>
#include <iostream>

using namespace std;

const int N = 5010;
int s[N][N];

int main()

    int N, R;
    scanf("%d %d", &N, &R);
    R = min(R, 5001);
    
    while (N--)
    
        int x, y, w;
        scanf("%d %d %d", &x, &y, &w);
        x++, y++;
        s[x][y] += w;
    
    
    for (int i = 1; i <= 5001; i++)
        for (int j = 1; j <= 5001; j++)
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
            
    int res = 0;
    for (int i = R; i <= 5001; i++)
        for (int j = R; j <= 5001; j++)
            res = max(res, s[i][j] - s[i - R][j] - s[i][j - R] + s[i - R][j - R]);
            
    printf("%d\\n", res);
    
    return 0;

③细节分析

  • 二位前缀和公式

    // 构造前缀和
    s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
    
    // 求一个边长为R - 1的矩形区域的值
    res = s[i][j] - s[i - R][j] - s[i][j - R] + s[i - R][j - R]
    
  • R超过5001就无意义,因为坐标最大值就只有5000

    R = min(R, 5001);
    
  • 读入数据

    while (N--)
    
        int x, y, w;
        scanf("%d %d %d", &x, &y, &w);
        x++, y++; // 前缀和下标要从1开始,因为会用到i - 1, j - 1,更好处理边界情况
        s[x][y] += w; // 不同目标可能在同一位置
    
    
  • 构造二位前缀和

    for (int i = 1; i <= 5001; i++) // 下标统一+1,故最大可以到5001
        for (int j = 1; j <= 5001; j++)
        s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
    
  • 利用二维前缀和求值

    int res = 0;
    for (int i = R; i <= 5001; i++) // 轰炸区域为RxR,故最小坐标从R开始
        for (int j = R; j <= 5001; j++)
            res = max(res, s[i][j] - s[i - R][j] - s[i][j - R] + s[i - R][j - R]);
    

以上是关于算法刷题AcWing 99. 激光炸弹——前缀和的主要内容,如果未能解决你的问题,请参考以下文章

Acwing 99 激光炸弹 (二维前缀和)

ACwing99 激光炸弹 二维前缀和

99. 激光炸弹

AcWing基础算法课Level-3 第一讲 基础算法

99. 激光炸弹二维前缀和

99. 激光炸弹