目录

1478:安排邮筒(2190 分)

力扣第 28 场双周赛第 4 题

题目

给你一个房屋数组houses 和一个整数 k ,其中 houses[i] 是第 i 栋房子在一条街上的位置,现需要在这条街上安排 k 个邮筒。

请你返回每栋房子与离它最近的邮筒之间的距离的 最小 总和。

答案保证在 32 位有符号整数范围以内。

示例 1:

输入:houses = [1,4,8,10,20], k = 3
输出:5
解释:将邮筒分别安放在位置 3, 9 和 20 处。
每个房子到最近邮筒的距离和为 |3-1| + |4-3| + |9-8| + |10-9| + |20-20| = 5 。

示例 2:

输入:houses = [2,3,5,12,18], k = 2
输出:9
解释:将邮筒分别安放在位置 3 和 14 处。
每个房子到最近邮筒距离和为 |2-3| + |3-3| + |5-3| + |12-14| + |18-14| = 9 。

示例 3:

输入:houses = [7,4,6,1], k = 1
输出:8

示例 4:

输入:houses = [3,6,14,10], k = 4
输出:0

提示:

  • n == houses.length
  • 1 <= n <= 100
  • 1 <= houses[i] <= 10^4
  • 1 <= k <= n
  • 数组 houses 中的整数互不相同。

分析

#1

  • 令 f(i,j) 代表安排 i 个邮筒,houses[:j] 的最小距离和
  • 遍历最后一个邮筒覆盖的房子范围 [a,j],转为子问题 f(i-1,a-1)
  • 计算一个邮筒覆盖区间 [a,b] 的最小和,将邮筒放在区间中位数即可,可以预处理前缀和,快速计算得到
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution:
    def minDistance(self, houses: List[int], k: int) -> int:
        def w(a,b):
            return p[b+1]-p[(a+b+1)//2]-(p[(a+b)//2+1]-p[a])
        A = sorted(houses)
        n = len(A)
        p = [0]+list(accumulate(A))
        f = [0]+[inf]*n
        for i in range(1,k+1):
            g = [0]+[inf]*n
            for j in range(1,n+1):
                for a in range(j):
                    g[j] = min(g[j],f[a]+w(a,j-1))
            f = g
        return f[-1]

520 ms

#2

  • 区间代价函数 w 满足四边形不等式,可以利用决策单调性优化,详细见 四边形不等式优化
  • 可以用分治,但更通用的是二分队列
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution:
    def minDistance(self, houses: List[int], k: int) -> int:
        def quad():
            def w(a,b):
                return f[a]+p[b]-p[(a+b)//2]-(p[(a+b+1)//2]-p[a])
            g = [0]+[inf]*n
            Q = deque([[0,1,n]])
            for j in range(1,n+1):
                while Q and Q[0][2]<j:
                    Q.popleft()
                g[j] = w(Q[0][0],j)
                while Q and w(j,Q[-1][1])<w(Q[-1][0],Q[-1][1]):
                    Q.pop()
                if not Q:
                    Q.append([j,j+1,n])
                else:
                    a = bisect_left(range(Q[-1][2]+1),True,j,key=lambda a:w(j,a)<w(Q[-1][0],a))
                    Q[-1][2] = a-1
                    if a<=n:
                        Q.append([j,a,n])
            return g
        A = sorted(houses)
        n = len(A)
        p = [0]+list(accumulate(A))
        f = [0]+[inf]*n
        for i in range(1,k+1):
            f = quad()
        return f[-1]

243 ms

#3

解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution:
    def minDistance(self, houses: List[int], k: int) -> int:
        def quad(mid):
            def w(a,b):
                return mid+g[a]+p[b]-p[(a+b)//2]-(p[(a+b+1)//2]-p[a])
            g = [0]+[inf]*n
            h = [0]*(n+1)
            Q = deque([[0,1,n]])
            for j in range(1,n+1):
                while Q and Q[0][2]<j:
                    Q.popleft()
                g[j] = w(Q[0][0],j)
                h[j] = h[Q[0][0]]+1
                while Q and w(j,Q[-1][1])<w(Q[-1][0],Q[-1][1]):
                    Q.pop()
                if not Q:
                    Q.append([j,j+1,n])
                else:
                    a = bisect_left(range(Q[-1][2]+1),True,j,key=lambda a:w(j,a)<w(Q[-1][0],a))
                    Q[-1][2] = a-1
                    if a<=n:
                        Q.append([j,a,n])
            return g,h
        A = sorted(houses)
        n = len(A)
        p = [0]+list(accumulate(A))
        l,r = 0,p[-1]
        while l<=r:
            mid = (l+r)//2
            g,h = quad(mid)
            if h[-1]<=k:
                res = g[-1]-mid*k
                if h[-1]==k:
                    break
                r = mid-1
            else:
                l = mid+1
        return res

95 ms