from functools import lru_cache
class Solution:
def minDistance(self, houses, k):
houses.sort()
n = len(houses)
# Precompute cost[i][j]: min total distance to cover houses[i:j+1] with 1 mailbox
cost = [[0]*n for _ in range(n)]
for i in range(n):
for j in range(i, n):
mid = (i + j) // 2
for x in range(i, j+1):
cost[i][j] += abs(houses[x] - houses[mid])
@lru_cache(None)
def dp(i, k):
if k == 0 and i == n:
return 0
if k == 0 or i == n:
return float('inf')
res = float('inf')
for j in range(i, n):
res = min(res, cost[i][j] + dp(j+1, k-1))
return res
return dp(0, k)
class Solution {
public:
int minDistance(vector<int>& houses, int k) {
sort(houses.begin(), houses.end());
int n = houses.size();
vector<vector<int>> cost(n, vector<int>(n, 0));
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
int mid = (i + j) / 2;
for (int x = i; x <= j; ++x)
cost[i][j] += abs(houses[x] - houses[mid]);
}
}
vector<vector<int>> dp(n+1, vector<int>(k+1, INT_MAX/2));
dp[n][0] = 0;
for (int i = n-1; i >= 0; --i) {
for (int m = 1; m <= k; ++m) {
for (int j = i; j < n; ++j) {
if (dp[j+1][m-1] != INT_MAX/2)
dp[i][m] = min(dp[i][m], cost[i][j] + dp[j+1][m-1]);
}
}
}
return dp[0][k];
}
};
class Solution {
public int minDistance(int[] houses, int k) {
Arrays.sort(houses);
int n = houses.length;
int[][] cost = new int[n][n];
for (int i = 0; i < n; ++i) {
for (int j = i; j < n; ++j) {
int mid = (i + j) / 2;
for (int x = i; x <= j; ++x)
cost[i][j] += Math.abs(houses[x] - houses[mid]);
}
}
int[][] dp = new int[n+1][k+1];
for (int i = 0; i <= n; ++i)
Arrays.fill(dp[i], Integer.MAX_VALUE/2);
dp[n][0] = 0;
for (int i = n-1; i >= 0; --i) {
for (int m = 1; m <= k; ++m) {
for (int j = i; j < n; ++j) {
dp[i][m] = Math.min(dp[i][m], cost[i][j] + dp[j+1][m-1]);
}
}
}
return dp[0][k];
}
}
var minDistance = function(houses, k) {
houses.sort((a, b) => a - b);
let n = houses.length;
let cost = Array.from({length: n}, () => Array(n).fill(0));
for (let i = 0; i < n; ++i) {
for (let j = i; j < n; ++j) {
let mid = Math.floor((i + j) / 2);
for (let x = i; x <= j; ++x)
cost[i][j] += Math.abs(houses[x] - houses[mid]);
}
}
let dp = Array.from({length: n+1}, () => Array(k+1).fill(Infinity));
dp[n][0] = 0;
for (let i = n-1; i >= 0; --i) {
for (let m = 1; m <= k; ++m) {
for (let j = i; j < n; ++j) {
dp[i][m] = Math.min(dp[i][m], cost[i][j] + dp[j+1][m-1]);
}
}
}
return dp[0][k];
};
Given an array houses
representing the locations of houses along a street (as integers), and an integer k
representing the number of mailboxes to allocate, the goal is to minimize the total distance between each house and its nearest mailbox. You must place exactly k
mailboxes at integer positions (not necessarily at house positions), and each house is assigned to its closest mailbox. The total distance is the sum of the distances from each house to its assigned mailbox.
k
mailboxes.
At first glance, the problem appears to require trying all possible ways to assign houses to mailboxes and all possible mailbox locations, which would be computationally expensive. The brute-force approach would consider every way to split the houses into k
groups and place a mailbox for each group, calculating the total distance for each possibility.
However, we can observe that for any group of houses, the optimal mailbox location is at the median house position, as this minimizes the sum of absolute differences (the total walking distance). Thus, the challenge reduces to partitioning the sorted list of houses into k
contiguous groups, and for each group, placing a mailbox at the median and summing the costs.
This insight leads to a dynamic programming (DP) approach, where we recursively try all ways to split the houses into k
groups and use memoization to avoid redundant calculations.
Let's break down the algorithm step-by-step:
houses
array.
(i, j)
, precompute the minimal total distance to cover houses[i]...houses[j]
with one mailbox. This is done by placing the mailbox at the median house position for that interval.
dp(i, k)
as the minimum total distance to allocate k
mailboxes starting from house i
.j
from i
to n-1
, compute the cost for houses i
to j
as one group, and recursively solve for the remaining houses and mailboxes.i = 0
and k
mailboxes.
This approach efficiently explores all valid groupings and leverages the optimal property of medians for minimizing distances.
Let's consider the input houses = [1, 4, 8, 10, 20]
and k = 3
.
[1, 4, 8, 10, 20]
.
[1]
: cost = 0 (only one house)[1, 4]
: median is 1 or 4, cost = |1-1| + |4-1| = 3[1, 4, 8]
: median is 4, cost = |1-4| + |4-4| + |8-4| = 3 + 0 + 4 = 7[1]
(cost 0), then solve for [4,8,10,20]
with 2 mailboxes.[1,4]
(cost 3), then solve for [8,10,20]
with 2 mailboxes, etc.
The "Allocate Mailboxes" problem leverages the property that the median minimizes the sum of absolute distances in a group. By precomputing costs for all intervals and using dynamic programming to optimally split the houses into k
groups, we can efficiently find the minimal total distance. The solution elegantly transforms a seemingly combinatorial problem into a manageable DP by exploiting mathematical and structural properties of the problem.