Leetcode每日一题:分割正方形 II

题目链接分割正方形 II

思路
今天我们讨论的是Leetcode上的题目——分割正方形 II。与昨天的题目类似,但有所不同。关键点在于重叠面积只计算一次,因此每个高度不能直接累加宽度计算面积,而是要通过线段组合计算。因此,每个高度需要先计算一次有效宽度。

我的思路是将所有线段都放到一个List中,通过引用快速移除顶部的线段,然后根据x值排序后累加计算有效宽度。获取有效宽度后,就跟昨天的题目一样处理了。

代码

    /**
     * 计算当前有效宽度
     * @param segList 此前包含的线段
     * @param addList 当前高度调整的线段
     * @return 有效宽度
     */
    private int combineLine(List<int> segList, List<int> addList) {
        // 遍历调整的线段,如果没有添加过(底边)则将第三个数置为1,否则(顶边)移除
        for (int add : addList) {
            if (add[2] == 1) {
                segList.remove(add);
            } else {
                add[2] = 1;
                segList.add(add);
            }
        }
        // 按照x轴排序,方便统计长度,宽度排不排序均可
        segList.sort((a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]);

        // 从左到右统计有效宽度,重叠部分不计
        int totalLen = 0;
        int loc = Integer.MIN_VALUE;
        for (int segment : segList) {
            if (segment[1] == 0) continue;
            int currentStart = segment[0];
            int currentEnd = segment[0] + segment[1];
            if (currentStart > loc) {
                totalLen += segment[1];
                loc = currentEnd;
            } else if (currentEnd > loc) {
                totalLen += currentEnd - loc;
                loc = currentEnd;
            }
        }

        return totalLen;
    }

    /**
     * 3454. 分割正方形 II
     * 与昨天不同的是,重叠面积只算一次,因此每个高度不能直接累加宽度计算面积,要通过线段组合计算。因此每个高度需要先计算一次有效宽度。
     * 我的思路是将所有线段都放到一个List中,通过引用快速移除顶的线段,然后根据x值排序后累加计算有效宽度。
     * @param squares 正方形的信息
     * @return 分割的高度
     * @link <a href="https://leetcode.cn/problems/separate-squares-ii">3454. 分割正方形 II</a>
     */
    public double separateSquares(int squares) {
        // 边记录,记录当前高度包含的块的 边-线段 键值对。底边增加一份引用,顶边再次增加**同一份引用**用于移除。
        TreeMap<Integer, List<int>> top = new TreeMap<>();
        for (int square : squares) {
            // 创建线段,将底边和顶边的引用分别添加到map中,第三个数用于判断是底还是顶
            int tmp = new int { square[0], square[2], 0 };
            top.compute(square[1], (k, v) -> {
                if (v == null) {
                    v = new ArrayList<>();
                }
                v.add(tmp);
                return v;
            });
            top.compute(square[1] + square[2], (k, v) -> {
                if (v == null) {
                    v = new ArrayList<>();
                }
                v.add(tmp);
                return v;
            });
        }
        int y = Integer.MIN_VALUE;
        int w = 0;
        double totalArea = 0;
        List<int> line = new LinkedList<>();
        TreeMap<Integer, Integer> map = new TreeMap<>();
        // 从下往上遍历每条边,计算当前有效宽度,并以此计算总面积
        while (!top.isEmpty()) {
            Map.Entry<Integer, List<int>> entry = top.pollFirstEntry();
            int cy = entry.getKey();
            int cw = combineLine(line, entry.getValue());
            map.put(cy, cw);
            totalArea += (double) w * (cy - y);
            y = cy;
            w = cw;
        }
        y = Integer.MIN_VALUE;
        w = 0;
        double half = totalArea / 2;
        // 从下往上遍历每条边,并计算面积,如果面积超过了一半,则通过面积计算高度并返回结果。
        while (!map.isEmpty()) {
            Map.Entry<Integer, Integer> entry = map.pollFirstEntry();
            int cy = entry.getKey();
            int cw = entry.getValue();
            if (w > 0) {
                double area = (double) (cy - y) * w;
                if (half <= area) {
                    return half / w + y;
                }
                half -= area;
            }
            y = cy;
            w = cw;
        }
        return -1;
    }

PS
看了下更快的方法,使用的是线段树结构,容我先学习一下。

标签: none

评论已关闭