机试题——疯长的草

题目描述

将种不同的草随机种在一块广漠无垠的二维平面上(直角坐标系内),给定二维数组 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;
}
相关推荐
arong_xu7 分钟前
现代C++锁介绍
c++·多线程·mutex
汤姆和杰瑞在瑞士吃糯米粑粑12 分钟前
【C++学习篇】AVL树
开发语言·c++·学习
DARLING Zero two♡19 分钟前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode
CodeClimb34 分钟前
【华为OD-E卷-木板 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
奶香臭豆腐1 小时前
C++ —— 模板类具体化
开发语言·c++·学习
游是水里的游1 小时前
【算法day19】回溯:分割与子集问题
算法
不想当程序猿_1 小时前
【蓝桥杯每日一题】分糖果——DFS
c++·算法·蓝桥杯·深度优先
cdut_suye1 小时前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
南城花随雪。2 小时前
单片机:实现FFT快速傅里叶变换算法(附带源码)
单片机·嵌入式硬件·算法