题目描述
将种不同的草随机种在一块广漠无垠的二维平面上(直角坐标系内),给定二维数组 points 表示第 0 天所有草的初始位置,第 i 项 points [i]=[xi, yi] 表第 0 天草 i 在点 [xi, yi]。每天,被草覆盖的点会向外蔓延到它上、下,左、右,左上、左下、右上、右下 8 个邻居点,注意,初始状态下,可能有多种草在同一点上。
现给定一个整数 M,问最小需要多少天,方能找到一点同时至少有 M 种草?
输入描述
第一行输入整数 M。(2<= M <= n) 第二行输入草的种数 n。(2 <= n <= 50) 后面连续 n 行输入草初始位置 [xi, yi]。(<= xi, vi <= 10^9)
输出描述
返口找到一点至少生长 M 种草的最少天数,找不到返回 0
用例输入
bash
2
2
2 1
6 2
bash
2
解释:在第2天,点(4,0)、(4,1)、(4,2)与(4,3)将有2种草。
bash
2
3
2 1
6 2
100 100
bash
2
尽管第三种草起始位置较远,但在第2天,前两种草仍可以满足条件。
解题思路
由于草的扩散是向8个方向无限扩展,且坐标范围较大(xi, yi ≤ 10^9),直接模拟草的扩散过程是不现实的。因此需要一种高效的方法来确定最小需要多少天才能满足条件。
使用二分法确定最小的天数 d,使得存在至少一个点被 M 种草覆盖。对于每种草,其在第 d 天时覆盖的区域为一个边长为 2d + 1 的正方形,中心为草的初始位置。
由于坐标范围巨大,无法在二维数组中直接标记覆盖区域。通过离散化坐标,将所有可能影响到的区域映射到一个较小的二维网格上。
还涉及到对于一个区间的查找和更新问题,对于不同的天数,在二维数组中更新不同的正方形面积,此处使用二维差分数组。
一维差分数组
差分数组定义:给定一个一维数组 A,其差分数组 D 定义为 D[i] = A[i] - A[i-1](对于 i ≥ 1)。初始化时,通常将 D 全部置零。
区间更新:要对区间 [L, R] 进行加 C 的操作,可以执行:
bash
D[L] += C
D[R+1] -= C
还原原数组:通过计算差分数组的前缀和,可以还原出更新后的原数组
bash
A[0] = D[0]
A[i] = A[i-1] + D[i] for i ≥ 1
二维差分的扩展
二维差分将上述一维差分的思想扩展到二维空间,适用于处理二维平面上的区域更新与查询。
bash
D[x][y] = A[x][y]
- A[x-1][y]
- A[x][y-1]
+ A[x-1][y-1]
其中,A[x][y] 表示坐标 (x, y) 处的值。
区域更新:要对一个矩形区域 [x1, y1] 到 [x2, y2] 进行加 C 的操作,可以执行:
bash
D[x1][y1] += C
D[x2+1][y1] -= C
D[x1][y2+1] -= C
D[x2+1][y2+1] += C
还原原数组:通过计算差分矩阵的二维前缀和,可以还原出更新后的二维数组:
bash
for x from 0 to N-1:
for y from 0 to M-1:
if x > 0:
A[x][y] += A[x-1][y]
if y > 0:
A[x][y] += A[x][y-1]
if x > 0 and y > 0:
A[x][y] -= A[x-1][y-1]
A[x][y] += D[x][y]
要对一个矩形区域 [x1, y1] 到 [x2, y2] 进行加 C 的操作,可以执行以下四个更新:
增加左上角 (x1, y1):D[x1][y1] += C
减少右上角 (x1, y2 + 1):D[x1][y2 + 1] -= C
减少左下角 (x2 + 1, y1):D[x2 + 1][y1] -= C
增加右下角 (x2 + 1, y2 + 1):D[x2 + 1][y2 + 1] += C
代码
cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
#include<string>
#include<vector>
#include<unordered_map>
#include<unordered_set>
#include<queue>
#include<set>
#include<list>
using namespace std;
#define MAX 200
struct point {
int lx, ly, rx, ry;
};
vector<point> g(MAX);
int m, n;
int mp[MAX][MAX];
int x[MAX], y[MAX];
bool check(int d) {
memset(mp, 0, sizeof(mp)); //必须初始化
map<int, int> cx, cy;// x的边界 y的边界
// 差分是左闭右闭
for (int i = 0; i < n; i++) {
// 覆盖的矩阵区间
g[i] = { x[i] - d,y[i] - d,x[i] + d,y[i] + d };
//先标识差分数组的坐标
cx[g[i].lx]=0;
cx[g[i].rx+1]=0;
cy[g[i].ly]=0;
cy[g[i].ry+1]=0;
}
// map 自动排序好了 直接离散坐标编号
int xx = 0, yy = 0;
for (auto& t : cx) t.second = ++xx;
for (auto& t : cy) t.second = ++yy;
// 开始针对离散坐标构建差分数组
for (int i = 0; i < n; i++) {
int lx = cx[g[i].lx];
int rx = cx[g[i].rx + 1];
int ly = cy[g[i].ly];
int ry = cy[g[i].ry + 1];
/*
(lx,ly) (lx,ry)
(rx,ly) (rx,ry)
*/
mp[lx][ly]++;
mp[lx][ry]--;
mp[rx][ly]--;
mp[rx][ry]++;
}
// 对离散坐标进行计算
for (int i = 1; i <=xx; i++) {
for (int j = 1; j <=yy; j++) {
mp[i][j] += mp[i - 1][j] + mp[i][j - 1] - mp[i - 1][j - 1];
//cout << i << " " << j<<" " << mp[i][j] << endl;
if (mp[i][j] >= m) return true;
}
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> m >> n;
for (int i = 0; i < n; i++) {
cin >> x[i] >> y[i];
}
int l = 0, r = 10e9;
while (l < r) {
int mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
}