[HNOI2003]激光炸弹---二维前缀和

题目描述

一种新型的激光炸弹,可以摧毁一个边长为 m 的正方形内的所有目标。现在地图上有 n 个目标,用整数 xi​ , yi​ 表示目标在地图上的位置,每个目标都有一个价值 vi​ .激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为 m 的边必须与 x 轴, y 轴平行。若目标位于爆破正方形的边上,该目标不会被摧毁。

现在你的任务是计算一颗炸弹最多能炸掉地图上总价值为多少的目标。

输入格式

输入的第一行为整数 n 和整数 m,

接下来的 n 行,每行有 3 个整数 x,y,v,表示一个目标的坐标与价值。

输出格式

输出仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过 3276732767 )。

样例 #1

样例输入 #1

2 1
0 0 1
1 1 1

样例输出 #1

1

提示

数据规模与约定
  • 对于 100% 的数据,保证 1≤n≤10^4,0≤xi​,yi​≤5×10^3,1≤m≤5×10^3,1≤vi​<100。

解题思路

看完题目可知就是在一个二维数组中,随机在某个位置增加某个数,然后求在 m x m 的正方形区域内二维数组的和的最大值是多少?

对于这种需要求到某个矩阵的子矩阵就需要想到二维前缀和

我们先来回忆前缀和的初始化和求子矩阵的和的公式

S[ i ][ j ] = S[ i - 1 ][ j ] + S[ i ][ j - 1 ] - S[ i - 1 ][ j - 1 ] + a [ i ][ j ]; //前缀和初始化

S[ x2 ][ y2 ] - S[ x1 - 1 ][ y2 ] - S[ x2 ][ y1 - 1 ] + S[ x1 - 1 ][ y1 - 1 ] //求子矩阵的和

好,上代码!

#include 

using namespace std;

int n, m;
const int N = 5010;
int a[N][N];
//int s[N][N]; //用两个数组内存可能会超,只需要将s[N][N]替换为a[N][N]就可以了
int main(void)
{
    scanf("%d%d", &n, &m);


    while (n--)
    {
        int x = 0, y = 0;
        int v = 0;
        scanf("%d%d%d", &x, &y, &v);
        a[x + 1][y + 1] += v;  //因为前缀和的下标都是从1开始的(防止边界问题)
    }
  

    for (int i = 1; i < N; i++) //求前缀和,循环次数不一定为我这样的
    {
        for (int j = 1; j < N; j++)
        {
            a[i][j] = a[i][j - 1] + a[i - 1][j] + a[i][j] - a[i - 1][j - 1];
        }
    }

   
    
    int max = -1;

    //写法一,我个人是比较推荐写法二,数组不容易越界
    for (int i = 1; i < N - m; i++) //循环次数不一定为我这样的
    {
        for (int j = 1; j < N - m; j++)
        {
            //x2=i+m-1 y2=j+m-1        x1=i y1=j
            int r = a[i + m - 1][j + m - 1] - a[i - 1][j + m - 1] - a[i + m - 1][j - 1] + a[i - 1][j - 1];
            if (r >= max)
            {
                max = r;
            }
        }
    }
    /* 写法二
    for (int i = m; i < N ; i++)
    {
        for (int j = m; j < N ; j++) 
        {
            //x2 = i     y2 = j     x1 = i-m+1     y1 = j-m+1
            int r = a[i ][j ] - a[i ][j -m] - a[i - m ][j ] + a[i - m ][j - m];
            if (r >= max)
            {
                max = r;
            }
        }
    }*/

    printf("%d\n", max);
    return 0;
}

在做这题时,我在求子矩阵的和时,因为数组的边界出了很多问题,所以出错了可以先看看数组是否越界了

在排除错误的过程中还可以先打印 5行5列看看数组是怎么样的,分别在输入后求前缀和之前  和  求前缀和之后 打印

代码

for (int i = 1; i <= 5; i++)
{
	for (int j = 1; j <= 5; j++)
	{
		printf("%d ", a[i][j]);
	}
	printf("\n");
}

你可能感兴趣的:(算法,c++)