LCP 59. 搭桥过河
欢迎各位勇者来到力扣城,本次试炼主题为「搭桥过河」。
勇者面前有一段长度为 num
的河流,河流可以划分为若干河道。每条河道上恰有一块浮木,wood[i]
记录了第 i
条河道上的浮木初始的覆盖范围。
请问勇者跨越这条河流,最少需要花费多少「自然之力」。
示例 1:
输入:
num = 10, wood = [[1,2],[4,7],[8,9]]
输出:3
解释:如下图所示, 将 [1,2] 浮木移动至 [3,4],花费 2「自然之力」, 将 [8,9] 浮木移动至 [7,8],花费 1「自然之力」, 此时勇者可以顺着 [3,4]->[4,7]->[7,8] 跨越河流, 因此,勇者最少需要花费 3 点「自然之力」跨越这条河流 {:width=650px}
示例 2:
输入:
num = 10, wood = [[1,5],[1,1],[10,10],[6,7],[7,8]]
输出:10
解释: 将 [1,5] 浮木移动至 [2,6],花费 1「自然之力」, 将 [1,1] 浮木移动至 [6,6],花费 5「自然之力」, 将 [10,10] 浮木移动至 [6,6],花费 4「自然之力」, 此时勇者可以顺着 [2,6]->[6,6]->[6,6]->[6,7]->[7,8] 跨越河流, 因此,勇者最少需要花费 10 点「自然之力」跨越这条河流
示例 3:
输入:
num = 5, wood = [[1,2],[2,4]]
输出:0
解释:勇者不需要移动浮木,仍可以跨越这条河流
提示:
1 <= num <= 10^9
1 <= wood.length <= 10^5
wood[i].length == 2
1 <= wood[i][0] <= wood[i][1] <= num
原站题解
cpp 解法, 执行用时: 1412 ms, 内存消耗: 356 MB, 提交时间: 2023-09-17 11:43:54
class Solution { priority_queue<long long> L; priority_queue<long long, vector<long long>, greater<long long>> R; public: long long buildBridge(int num, vector<vector<int>>& wood) { L.push(wood[0][0]); R.push(wood[0][0]); long long biasL = 0, biasR = 0, ans = 0; for (int i = 1; i < wood.size(); i++) { biasL -= wood[i][1] - wood[i][0]; biasR += wood[i - 1][1] - wood[i - 1][0]; long long l0 = L.top() + biasL; long long r0 = R.top() + biasR; int x = wood[i][0]; if (x < l0) { ans += l0 - x; L.pop(); L.push(x - biasL); L.push(x - biasL); R.push(l0 - biasR); } else if (x > r0) { ans += x - r0; R.pop(); R.push(x - biasR); R.push(x - biasR); L.push(r0 - biasL); } else { L.push(x - biasL); R.push(x - biasR); } } return ans; } };
python3 解法, 执行用时: 3708 ms, 内存消耗: 59.8 MB, 提交时间: 2023-09-17 11:43:27
class Solution: def buildBridge(self, _num: int, wood: List[List[int]]) -> int: S = SlopeTrick() length = [b - a for a, b in wood] for i, (left, _) in enumerate(wood): S.addLeftOffset(-length[i]) if i > 0: S.addRightOffset(length[i - 1]) S.addAbsXMinusA(left) return S.getMinY() INF = int(1e18) class SlopeTrick: """ https://maspypy.com/slope-trick-1-%e8%a7%a3%e8%aa%ac%e7%b7%a8 上记の记事にもとづき、@caomeinaixiさんが実装したテンプレートです。 """ __slots__ = "_minY", "_leftTuring", "_rightTuring", "_leftOffset", "_rightOffset" def __init__( self, leftTuring: Optional[List[int]] = None, rightTuring: Optional[List[int]] = None ) -> None: self._minY = 0 # dp 最小値 self._leftTuring = [INF] if leftTuring is None else leftTuring # 左拐点 self._rightTuring = [INF] if rightTuring is None else rightTuring # 右拐点 self._leftOffset = 0 # 左拐点的平移量 self._rightOffset = 0 # 右拐点的平移量 def addAbsXMinusA(self, a: int) -> None: """|x-a|の加算:O(logn) 时间""" self.addXMinusA(a) self.addAMinusX(a) def addXMinusA(self, a: int) -> None: """(x-a)+の加算:O(logn) 时间 倾きの変化点に a が追加されます minYの変化はf(left0)に等しい """ if len(self._leftTuring) != 0: self._minY += max(0, self.leftTop - a) self._pushLeft(a) self._pushRight(self._popLeft()) def addAMinusX(self, a: int) -> None: """(a-x)+の加算:O(logn) 时间 倾きの変化点に a が追加されます minYの変化はf(right0)に等しい """ if len(self._rightTuring) != 0: self._minY += max(0, a - self.rightTop) self._pushRight(a) self._pushLeft(self._popRight()) def addY(self, delta: int) -> None: """yの加算:O(1) 时间""" self._minY += delta def addOffset(self, delta: int) -> None: """平移:O(1) 时间 g(x) = f(x - a) fをg に取り换える """ self._leftOffset += delta self._rightOffset += delta def addLeftOffset(self, delta: int) -> None: """左拐点の平移:O(1) 时间""" self._leftOffset += delta def addRightOffset(self, delta: int) -> None: """右拐点の平移:O(1) 时间""" self._rightOffset += delta def updateLeftMin(self) -> None: """累积 min:O(1) 时间 g(x) = min(f(y) | y <= x) fをg に取り换える rightTuringを空集合に取り换える """ self._rightTuring = [INF] def updateRightMin(self) -> None: """累积 min:O(1) 时间 g(x) = min(f(y) | y >= x) fをg に取り替える leftTuringを空集合に取り换える """ self._leftTuring = [INF] def updateWindowMin(self, leftDiff: int, rightDiff: int) -> None: """累积 min:O(1) 时间 g(x) = min(f(y) | `x - leftDiff <= y <= x - rightDiff`) fをg に取り替える 左侧集合・右侧集合それぞれを平行移动する left0, right0 => left0 + rightDiff, right0 + leftDiff """ self._leftOffset += rightDiff self._rightOffset += leftDiff def getMinY(self) -> int: """最小値の取得:O(1) 时间""" return self._minY def _pushLeft(self, a: int) -> None: heappush(self._leftTuring, -a + self._leftOffset) def _pushRight(self, a: int) -> None: heappush(self._rightTuring, a - self._rightOffset) def _popLeft(self) -> int: return -heappop(self._leftTuring) + self._leftOffset def _popRight(self) -> int: return heappop(self._rightTuring) + self._rightOffset @property def leftTop(self) -> int: """左侧の倾きの変化点の最大値left0の取得:O(1)时间""" return -self._leftTuring[0] + self._leftOffset @property def rightTop(self) -> int: """右侧の倾きの変化点の最小値right0の取得:O(1)时间""" return self._rightTuring[0] + self._rightOffset