本题跟410. 分割数组的最大值 差不多,该题的题解 。
思路:将这条街分为几块区域,每个区域安排一个邮筒。在一个区域中,要想邮筒到该区域所有房子的距离之和最小,邮筒应该安排在这些房子位置的中位数上,这时邮筒到它们的距离总和最小(可由绝对值不等式证明)。
首先先求出区域[i, j]
安排一个邮筒的到区域内各个房子距离之和最小值,然后dfs搜索所有划分点,求出所有划分中各区域到邮筒距离之和的最小值。
class Solution {
// cost[i][j]表示在区域[i, j]中安排一个邮筒到该区域其他房子的距离之和的最小值
int[][] cost;
int n;
public int minDistance(int[] houses, int m) {
Arrays.sort(houses);
n = houses.length;
cost = new int[n][n];
for(int i = 0; i < n; i++){
for(int j = i; j < n; j++){
for(int k = i; k <= j; k++){
cost[i][j] += Math.abs(houses[k] - houses[i+(j-i+1)/2]);
}
}
}
return helper(0, 0, m, new Integer[n+1][m+1]);
}
// [i, n)是待划分的区域,j表示当前第几个划分
public int helper(int i, int j, int m, Integer[][] memo){
if(memo[i][j] != null)
return memo[i][j];
// 划分结束且最后一组不为空,直接返回最后一组区域[i, n)要花费的代价
if(j + 1 == m && i < n)
return cost[i][n-1];
int min = Integer.MAX_VALUE / 2;
// 穷尽所有的划分点
for(int k = i; k < n; k++){
// [i, k]是当前划分,[k+1, n)是后面的划分
min = Math.min(min, cost[i][k] + helper(k+1, j+1, m, memo));
}
// 记忆
return memo[i][j] = min;
}
}
状态表示
状态计算/集合划分
假设最后一个划分位置在k,k∈[0,i]k\in[0, i]k∈[0,i],那么
f(i,j)=f(k−1,j−1)+cost[k][i]f(i,j)=f(k-1,j-1)+cost[k][i] f(i,j)=f(k−1,j−1)+cost[k][i]
表示将前k-1所房子分配j-1个邮筒的距离最小和加上本次分配的邮筒所产生的距离和。
class Solution {
int[][] cost;
int n;
public int minDistance(int[] houses, int m) {
Arrays.sort(houses);
n = houses.length;
cost = new int[n][n];
for(int i = 0; i < n; i++){
for(int j = i; j < n; j++){
for(int k = i; k <= j; k++){
cost[i][j] += Math.abs(houses[k] - houses[i+(j-i+1)/2]);
}
}
}
int[][] dp = new int[n][m+1];
for(int i = 0; i < n; i++)
dp[i][0] = Integer.MAX_VALUE / 2;
for(int i = 1; i < n; i++){
for(int j = 1; j <= m; j++){
dp[i][j] = Integer.MAX_VALUE / 2;
for(int k = 0; k <= i; k++){
int t = 0;
if(k != 0) t = dp[k-1][j-1];
dp[i][j] = Math.min(dp[i][j], t + cost[k][i]);
}
}
}
return dp[n-1][m];
}
}
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=h9nz2zbl70v8