力场重叠问题

求解思路

这道题的本质是求平面上被最多矩形覆盖的点的覆盖次数。

直观的想法是遍历平面上每个点统计覆盖次数,但坐标范围可能很大,暴力枚举会超时。

我们可以观察到虽然坐标值可能很大,但真正有意义的分界线只有每个矩形的四条边,最多2n条竖线和2n条横线,它们将平面切分成最多O(n²)个小矩形区域,每个区域内的覆盖次数是相同的。

我们可以用坐标压缩把这些关键坐标映射到较小的编号上,将问题规模从无限大的平面压缩到有限的网格。

接下来的问题是如何快速标记每个矩形覆盖了哪些网格,如果逐个网格标记会是O(n³)复杂度。

对于一个矩形区域(a,b)到(c,d),我们只需要在左上角(a,b)加1,右下角外侧(c+1,d+1)加1,右上角外侧(c+1,b)减1,左下角外侧(a,d+1)减1,这样做完所有矩形的标记后,从左上到右下做二维前缀和还原,每个格子的值就是它被多少个矩形覆盖的次数。

代码实现

java 复制代码
public static int fieldOfGreatestBlessing(int[][] fields) {
    int n = fields.length;
    // 收集所有矩形的边界坐标
    long[] xs = new long[n << 1];
    long[] ys = new long[n << 1];
    for (int i = 0, k = 0, p = 0; i < n; i++) {
        long x = fields[i][0];
        long y = fields[i][1];
        long r = fields[i][2];
        // 坐标乘2避免小数,左边界和右边界
        xs[k++] = (x << 1) - r;
        xs[k++] = (x << 1) + r;
        ys[p++] = (y << 1) - r;
        ys[p++] = (y << 1) + r;
    }
    
    // 坐标压缩:排序去重
    int sizex = sort(xs);
    int sizey = sort(ys);
    
    // 二维差分数组
    int[][] diff = new int[sizex + 2][sizey + 2];
    
    // 对每个矩形在差分数组上标记
    for (int i = 0; i < n; i++) {
        long x = fields[i][0];
        long y = fields[i][1];
        long r = fields[i][2];
        // 找到压缩后的坐标编号
        int a = rank(xs, (x << 1) - r, sizex);
        int b = rank(ys, (y << 1) - r, sizey);
        int c = rank(xs, (x << 1) + r, sizex);
        int d = rank(ys, (y << 1) + r, sizey);
        // 二维差分标记
        add(diff, a, b, c, d);
    }
    
    // 还原真实覆盖次数并求最大值
    int ans = 0;
    for (int i = 1; i < diff.length; i++) {
        for (int j = 1; j < diff[0].length; j++) {
            diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1];
            ans = Math.max(ans, diff[i][j]);
        }
    }
    return ans;
}

// 排序去重,返回有效长度
public static int sort(long[] nums) {
    Arrays.sort(nums);
    int size = 1;
    for (int i = 1; i < nums.length; i++) {
        if (nums[i] != nums[size - 1]) {
            nums[size++] = nums[i];
        }
    }
    return size;
}

// 二分查找坐标对应的压缩编号
public static int rank(long[] nums, long v, int size) {
    int l = 0, r = size - 1;
    int ans = 0;
    while (l <= r) {
        int m = (l + r) / 2;
        if (nums[m] >= v) {
            ans = m;
            r = m - 1;
        } else {
            l = m + 1;
        }
    }
    return ans + 1;  // 编号从1开始
}

// 二维差分标记矩形区域
public static void add(int[][] diff, int a, int b, int c, int d) {
    diff[a][b] += 1;
    diff[c + 1][d + 1] += 1;
    diff[c + 1][b] -= 1;
    diff[a][d + 1] -= 1;
}

如果觉得有帮助,欢迎点赞、关注、转发~

相关推荐
guodingdingh1 小时前
软件开发工作问题总结0718
java·开发语言·数据库
咖啡八杯3 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
GuWenyue3 小时前
传统Agent工具两大痛点!300行代码落地MCP跨语言工具,彻底解耦LLM与工具
前端·人工智能·算法
优橙教育3 小时前
5G网优培训 vs Java开发:转行选哪个?
java·开发语言·5g
糖果店的幽灵3 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
我叫洋洋4 小时前
C ++ [ hello world ]
c语言·c++·算法
腻害兔4 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
SeaTunnel5 小时前
从 Python Script 地狱到标准化数据集成框架
大数据·开发语言·python·程序员·代码·seatunnel
碎光拾影5 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
奋发向前wcx5 小时前
y1,y2总复习笔记5 2026.7.19
数据结构·笔记·算法