题目
给你一个房屋数组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 中的整数互不相同。
分析
首先,考虑这个问题:给定一个数组,找到一个点,使得该点到数组中所有数的距离和最小。
比如升序序列 [a, b, c],求 。首先
, 由绝对值不等式,
, 即当 x = b 时,上面的式子有最小值 |c - a|。
此外当有偶数的元素时,可以取最中间的两个元素中的任何一个。
由此我们得到,如果只安排一个邮箱的话,将其安排到序列的中位数即可。
其次,定义: 表示前 i 个房子,用了 j 个邮箱,距离和的最小值。
状态转移:, 其中
表示第 i 个房子到第 j 个房子中加入一个邮箱得到距离和的最小值。
解释:考虑 j - 1 个邮箱负责的房子的个数。由于第 j 个邮箱必须要使用,所以 j - 1 个邮箱最多负责前 i - 1 个房子,第 i 个房子由第 j 个邮箱负责; j - 1 个邮箱最少负责 j - 1 个房子,第 j 到 i 个房子由第 j 个邮箱负责。
注:上面所说的第 i 个房子是 houses[i - 1],以此类推。
代码
class Solution {
public int minDistance(int[] houses, int k) {
Arrays.sort(houses);
int n = houses.length;
// rec[i][j] 表示第 i 个房子(houses[i - 1]) 到第 j 个房子(houses[j - 1]) 中间放一个邮筒,距离和的最小值
int[][] rec = new int[n + 1][n + 1];
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
int mid = i + j >> 1;
for (int x = i; x <= j; x++) {
rec[i][j] += Math.abs(houses[mid - 1] - houses[x - 1]);
}
}
}
// dp[i][j] 表示前 i 个房子(houses[0] ~ houses[i - 1]), 用了 j 个邮筒,距离和的最小值
int[][] dp = new int[n + 1][k + 1];
// 初始化 dp[1][1] ~ dp[n][1], dp[1][2] ~ dp[1][k]
for (int i = 1; i <= n; i++) {
dp[i][1] = rec[1][i]; // 从第一个房子到第 i 个房子
}
for (int j = 2; j <= k; j++) {
dp[1][j] = Integer.MAX_VALUE;
}
// dp[i][j] = min{dp[x][j - 1] + rec[x + 1][i] | j - 1 <= x <= i - 1}
// x = j - 1 表示前 j - 1 个房子各对应一个邮筒, 第 j 个房子到第 i 个房子对应第 j 个邮筒
// x = i - 1 表示前 i - 1 个房子对应前 j - 1 个邮筒,第 i 个房子对应第 j 个邮筒
for (int i = 2; i <= n; i++) {
for (int j = 2; j <= k && j <= i; j++) {
dp[i][j] = Integer.MAX_VALUE;
for (int x = j - 1; x <= i - 1; x++) {
dp[i][j] = Math.min(dp[x][j - 1] + rec[x + 1][i], dp[i][j]);
}
}
}
return dp[n][k];
}
}