heap
binary search
graph
line sweep
sort
segment tree
intervals
dijkstra
]
BinarySearch 0553 Take All
Problem statement
https://binarysearch.com/problems/Take-All/
Solution
First of all notice, tha what is asked in this problem is find the cheapest union of segments, such that after we remove repetitions we have segment [0, N-1]
This problem have several ideas how you can solve it:
- Let us define by
dp[i]the minimum cost to cover[0, i]segment. Then how we can calculatedp[i]? Imagine, that current segment is[s, e]. Then we need to look at all segments[s1, e1], such thate1 >= s-1, that is we can connect them. How we can choose the best of them, because potentiall there can beO(n)candidates and this will lead toO(n^2)complexity?
The clue idea is to keep the heap of candidates, where they are sorted by key dp[e1] + prefix[e1], where prexix[e1] is what cost we need to pay to remove elements upty e1. Why we use this key? Imagine that we have two candidates [s1, e1] and [s2, e2], which one to choose? We compare dp[e1] + prefix[e1] and dp[e2] + prefix[e2] and choose the smallest one: in the first one we have prefix[s:e1] term and in second we have prefix[s:e2] term which we pay for.
Complexity
Time complexity is O(n log n), space is O(n).
Code
class Solution:
def solve(self, A, B):
n = len(B)
A.sort()
acc = list(accumulate(B))
dp = [float("inf")] * n
pq = [(0, -1, 0)]
for s, e, cost in A:
while pq and pq[0][1] < s-1: heappop(pq) #lazy deletion
if len(pq) == 0: return -1
p = pq[0]
dp[e] = min(dp[e], p[2] + cost + (acc[p[1]] - acc[s] + B[s]) * (p[1] >= s))
heappush(pq, (dp[e] + acc[e], e, dp[e]))
return dp[-1] if dp[-1] != float("inf") else -1
Solution 2
There is also solution, using graphs! We can see at out problem as finding the shortest path between nodes 0 and n-1, where we allowe to go in positive direction as well as we can make stesp backward with length 1. Why this is true, because for every valid set of segments there will never be a point where 3 points intersect. So, we just use Dijkstra algorithm to find shortest path.
Complexity
Time complexity is O(n log n), space complexity is O(n).
Code
class Solution:
def solve(self, A, B):
n = len(B)
G = defaultdict(list)
for s, e, w in A:
G[s].append([e + 1, w])
for i, r in enumerate(B):
G[i + 1].append([i, r])
dist = [float('inf')] * (n + 1)
dist[0] = 0
heap = [(0, 0)]
while heap:
min_dist, idx = heappop(heap)
if idx == n: return min_dist
for neibh, weight in G[idx]:
cand = min_dist + weight
if cand < dist[neibh]:
dist[neibh] = cand
heappush(heap, (cand, neibh))
return -1
Remark
There is also solution using monotonic queue + binary search, but I am not sure how it works here. Also there is solution with segment trees, which similar to solution 1, but instead of heaps we use segment tree: we store at index i in our segment tree dp[i] + acc[i], and then we can answer queries fast.