Skip to content

Files

Latest commit

c29b144 · May 17, 2024

History

History

LCP 78. 城墙防线

comments difficulty edit_url
true
中等

题目描述

在探险营地间,小扣意外发现了一片城墙遗迹,在探索期间,却不巧遇到迁徙中的兽群向他迎面冲来。情急之下小扣吹响了他的苍蓝笛,随着笛声响起,遗迹中的城墙逐渐发生了横向膨胀。 已知 rampart[i] = [x,y] 表示第 i 段城墙的初始所在区间。当城墙发生膨胀时,将遵循以下规则:

  • 所有的城墙会同时膨胀相等的长度;
  • 每个城墙可以向左、向右或向两个方向膨胀。

小扣为了确保自身的安全,需要在所有城墙均无重叠的情况下,让城墙尽可能的膨胀。请返回城墙可以膨胀的 最大值

注意:

  • 初始情况下,所有城墙均不重叠,且 rampart 中的元素升序排列;
  • 两侧的城墙可以向外无限膨胀。

示例 1:

输入:rampart = [[0,3],[4,5],[7,9]]

输出:3

解释:如下图所示: rampart[0] 向左侧膨胀 3 个单位; rampart[2] 向右侧膨胀 3 个单位; rampart[1] 向左侧膨胀 1 个单位,向右膨胀 2 个单位。 不存在膨胀更多的方案,返回 3。 image.png{:width=750px}

示例 2:

输入:rampart = [[1,2],[5,8],[11,15],[18,25]]

输出:4

提示:

  • 3 <= rampart.length <= 10^4
  • rampart[i].length == 2
  • 0 <= rampart[i][0] < rampart[i][1] <= rampart[i+1][0] <= 10^8

解法

方法一:二分查找

我们注意到,如果一个膨胀值 x 满足条件,那么所有小于 x 的值也都满足条件,这存在着单调性。因此我们可以使用二分查找的方法,找到最大的满足条件的膨胀值。

我们定义二分查找的左边界 l e f t = 0 , r i g h t = r a m p a r t [ 1 ] [ 0 ] r a m p a r t [ 0 ] [ 1 ] + r a m p a r t [ 2 ] [ 0 ] r a m p a r t [ 1 ] [ 1 ] ,其中 r a m p a r t [ i ] [ 0 ] 表示第 i 段城墙的左端点,$rampart[i][1]$ 表示第 i 段城墙的右端点。

接下来,我们开始进行二分查找。每一次,我们求出当前的中间值 m i d ,并判断其是否满足条件。如果满足条件,那么我们就将左边界 l e f t 更新为 m i d ,否则将右边界 r i g h t 更新为 m i d 1 。在二分查找结束后,我们返回左边界 l e f t 即可。

那么问题的关键在于如何判断一个值 w 是否满足条件。显然,我们可以用贪心的策略,每次先尽可能往左膨胀,如果还有剩余的膨胀值,再往右膨胀,如果在向右膨胀的过程中,发生了重叠,那么说明当前的膨胀值 w 不满足条件,直接返回 false 即可。否则,遍历结束,返回 true

时间复杂度 O ( n × log M ) ,其中 n M 分别是数组 r a m p a r t 的长度和二分查找的右边界,本题中右边界最大为 r a n g e = r a m p a r t [ 1 ] [ 0 ] r a m p a r t [ 0 ] [ 1 ] + r a m p a r t [ 2 ] [ 0 ] r a m p a r t [ 1 ] [ 1 ] 。空间复杂度 O ( 1 )

Python3

class Solution:
    def rampartDefensiveLine(self, rampart: List[List[int]]) -> int:
        def check(w: int) -> bool:
            last = rampart[0][1]
            for i in range(1, len(rampart) - 1):
                x, y = rampart[i]
                a = x - last
                b = max(w - a, 0)
                if y + b > rampart[i + 1][0]:
                    return False
                last = y + b
            return True

        left, right = 0, rampart[1][0] - rampart[0][1] + rampart[2][0] - rampart[1][1]
        while left < right:
            mid = (left + right + 1) >> 1
            if check(mid):
                left = mid
            else:
                right = mid - 1
        return left

Java

class Solution {
    private int[][] rampart;

    public int rampartDefensiveLine(int[][] rampart) {
        this.rampart = rampart;
        int left = 0, right = rampart[1][0] - rampart[0][1] + rampart[2][0] - rampart[1][1];
        while (left < right) {
            int mid = (left + right + 1) >> 1;
            if (check(mid)) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }

    private boolean check(int w) {
        int last = rampart[0][1];
        for (int i = 1; i < rampart.length - 1; ++i) {
            int x = rampart[i][0], y = rampart[i][1];
            int a = x - last;
            int b = Math.max(w - a, 0);
            if (y + b > rampart[i + 1][0]) {
                return false;
            }
            last = y + b;
        }
        return true;
    }
}

C++

class Solution {
public:
    int rampartDefensiveLine(vector<vector<int>>& rampart) {
        int left = 0, right = rampart[1][0] - rampart[0][1] + rampart[2][0] - rampart[1][1];
        auto check = [&](int w) {
            int last = rampart[0][1];
            for (int i = 1; i < rampart.size() - 1; ++i) {
                int x = rampart[i][0], y = rampart[i][1];
                int a = x - last;
                int b = max(w - a, 0);
                if (y + b > rampart[i + 1][0]) {
                    return false;
                }
                last = y + b;
            }
            return true;
        };

        while (left < right) {
            int mid = (left + right + 1) >> 1;
            if (check(mid)) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }
};

Go

func rampartDefensiveLine(rampart [][]int) int {
	check := func(w int) bool {
		last := rampart[0][1]
		for i := 1; i < len(rampart)-1; i++ {
			x, y := rampart[i][0], rampart[i][1]
			a := x - last
			b := max(w-a, 0)
			if y+b > rampart[i+1][0] {
				return false
			}
			last = y + b
		}
		return true
	}

	left, right := 0, rampart[1][0]-rampart[0][1]+rampart[2][0]-rampart[1][1]
	for left < right {
		mid := (left + right + 1) >> 1
		if check(mid) {
			left = mid
		} else {
			right = mid - 1
		}
	}
	return left
}

TypeScript

function rampartDefensiveLine(rampart: number[][]): number {
    const check = (w: number): boolean => {
        let last = rampart[0][1];
        for (let i = 1; i < rampart.length - 1; ++i) {
            const [x, y] = rampart[i];
            const a = x - last;
            const b = Math.max(w - a, 0);
            if (y + b > rampart[i + 1][0]) {
                return false;
            }
            last = y + b;
        }
        return true;
    };
    let left = 0;
    let right = rampart[1][0] - rampart[0][1] + rampart[2][0] - rampart[1][1];
    while (left < right) {
        const mid = (left + right + 1) >> 1;
        if (check(mid)) {
            left = mid;
        } else {
            right = mid - 1;
        }
    }
    return left;
}