leetcode
leetcode 3051 ~ 3100
城墙防线

城墙防线

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 192 ms, 内存: 20.0 MB


/*
 * Problem Statement:
 * Given an array of intervals representing ramparts, each rampart can expand to the left, right, or both directions.
 * The goal is to determine the maximum expansion value such that no ramparts overlap after expansion.
 * Initial intervals are non-overlapping and sorted in ascending order.
 */

import java.util.Arrays;

public class RampartExpansionStream {
    public int maxExpansion(int[][] rampart) {
        int left = 0, right = Integer.MAX_VALUE;
        while (left < right) {
            int mid = (left + right + 1) / 2;
            if (canExpand(rampart, mid)) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }

    private boolean canExpand(int[][] rampart, int length) {
        return Arrays.stream(rampart, 1, rampart.length)
                     .allMatch(interval -> interval[0] - rampart[Arrays.asList(rampart).indexOf(interval) - 1][1] >= 2 * length);
    }

    public static void main(String[] args) {
        RampartExpansionStream res = new RampartExpansionStream();
        int[][] rampart1 = {{0, 3}, {4, 5}, {7, 9}};
        int[][] rampart2 = {{1, 2}, {5, 8}, {11, 15}, {18, 25}};
        System.out.println(res.maxExpansion(rampart1)); // Output: 3
        System.out.println(res.maxExpansion(rampart2)); // Output: 4
    }
}

解释

方法:

这个解法基于二分查找的方法来确定城墙可以膨胀的最大距离。首先,创建一个列表 `lst`,每个元素表示相邻城墙间的初始间隔。然后,定义一个辅助函数 `check(num)` 来验证给定的膨胀距离 `num` 是否可行,即是否能保持城墙间无重叠。`check` 函数通过逐个调整剩余的间隔来实现这一点。如果某个间隔小于 `num`,则从下一间隔中减去不足的部分。如果所有间隔都可以调整以满足条件,则返回 `True`。在主函数中,通过二分查找的方式不断调整 `num`,直到找到可能的最大膨胀距离。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
在实现`check`函数时,为什么需要使用副本`temp`,而不是直接在原始的`lst`上操作?
在`check`函数中使用`lst`的副本`temp`是为了保证每次调用`check`函数时`lst`都处于未被修改的初始状态。这样可以避免前一次调用`check`对`lst`的修改影响到后续的调用,确保每次`check`的判断都是基于相同的起始条件。如果直接在`lst`上操作,那么经过一次`check`后,`lst`中的数据可能会被永久改变,导致后续的二分查找逻辑错误。
🦆
二分查找中,为什么选择`right`的初始值为`min(lst[i] + lst[i - 1] for i in range(1, len(lst)))`?这样的选择有什么特殊的意义或优势吗?
选择`right`的初始值为`min(lst[i] + lst[i - 1] for i in range(1, len(lst)))`是为了确保我们不会从一个不可能成功的膨胀距离开始查找。这个值是基于考虑最短的两个连续间隔的和,因为最大可能的膨胀距离不可能超过任何两个间隔和的最小值。这样做可以有效减小二分查找的范围,提高算法的效率,同时避免不必要的`check`调用。
🦆
在二分查找的过程中,如果`check(mid)`返回`True`,为什么更新`left = mid`而不是`left = mid + 1`?
在二分查找中,当`check(mid)`返回`True`时,更新`left = mid`而不是`left = mid + 1`是为了保证不错过最大的可能值。这种做法是因为我们正在搜索最大的满足条件的`mid`。如果我们立即使用`left = mid + 1`,可能会在某些情况下跳过最大可能的膨胀距离。而更新`left = mid`确保了在下一次循环中,该`mid`值还有机会被再次考虑,从而确保找到真正的最大值。

相关问题