https://leetcode.com/problems/allocate-mailboxes/description/
给定一个长 n n n的数组 h h h,表示 n n n个房子的坐标,题目保证坐标都是正整数。再给定一个小于等于 n n n的正整数 m m m,允许在数轴上放 m m m个邮箱,设 g [ i ] g[i] g[i]为下标为 i i i的房子与与其最近的邮箱的距离,问 ∑ i = 1 n g [ i ] \sum_{i=1}^n g[i] ∑i=1ng[i]可以取得的最小值。
设 h h h的前缀和数组为 p p p,先预处理出二维数组 c [ i ] [ j ] c[i][j] c[i][j],含义是如果 h [ i : j ] h[i:j] h[i:j]中的房子都由一个邮箱管辖,那么 c [ i ] [ j ] c[i][j] c[i][j]即为 ∑ t = i j g [ t ] \sum_{t=i}^j g[t] ∑t=ijg[t]能取得的最小值。那么显然邮箱应该取在中位数的位置,即 h [ ⌊ i + j 2 ⌋ ] h[\lfloor\frac{i+j}{2}\rfloor] h[⌊2i+j⌋],设 l = ⌊ i + j 2 ⌋ l=\lfloor\frac{i+j}{2}\rfloor l=⌊2i+j⌋,所以: c [ i ] [ j ] = ∑ t = i j ∣ h [ t ] − h [ l ] ∣ = ∑ t = i l ( h [ l ] − h [ t ] ) + ∑ t = l + 1 j ( h [ t ] − h [ l ] ) = ( 2 l − i − j + 1 ) h [ l ] − ( p [ l + 1 ] − p [ i ] ) + ( p [ j + 1 ] − p [ l + 1 ] ) = ( 2 l − i − j + 1 ) h [ l ] + p [ i ] + p [ j + 1 ] − 2 p [ l + 1 ] c[i][j]=\sum_{t=i}^j |h[t]-h[l]|=\\\sum_{t=i}^{l} (h[l]-h[t])+\sum_{t=l+1}^{j} (h[t]-h[l])\\=(2l-i-j+1)h[l]-(p[l+1]-p[i])+(p[j+1]-p[l+1])\\=(2l-i-j+1)h[l]+p[i]+p[j+1]-2p[l+1] c[i][j]=t=i∑j∣h[t]−h[l]∣=t=i∑l(h[l]−h[t])+t=l+1∑j(h[t]−h[l])=(2l−i−j+1)h[l]−(p[l+1]−p[i])+(p[j+1]−p[l+1])=(2l−i−j+1)h[l]+p[i]+p[j+1]−2p[l+1]接着,设 f [ i ] [ j ] f[i][j] f[i][j]是只考虑 h [ 0 : i ] h[0:i] h[0:i]这些房子,且开不多于 j j j个邮箱的情况下,能取到的最小总距离。那么所有的方案可以按照最右边那个邮箱的管辖范围来分类,如果其管辖了 [ 0 : i ] [0:i] [0:i],则可以用 c [ 0 ] [ i ] c[0][i] c[0][i]来更新 f [ i ] [ j ] f[i][j] f[i][j];如果其管辖了 [ k : i ] , k > 0 [k:i],k>0 [k:i],k>0,则可以用 c [ k ] [ i ] + f [ k − 1 ] [ j − 1 ] c[k][i]+f[k-1][j-1] c[k][i]+f[k−1][j−1]来更新 f [ i ] [ j ] f[i][j] f[i][j]。最后返回 f [ n − 1 ] [ m ] f[n-1][m] f[n−1][m]即可。代码如下:
class Solution {
public:
int minDistance(vector<int>& h, int m) {
sort(h.begin(), h.end());
int n = h.size();
int f[n][m + 1], cost[n][n], p[n + 1];
memset(f, 0, sizeof f);
p[0] = 0;
for (int i = 0; i < n; i++) p[i + 1] = h[i] + p[i];
for (int i = 0; i < n; i++)
for (int j = i; j < n; j++) {
int mid = (i + j) / 2;
cost[i][j] =
(i + j & 1 ^ 1) * h[mid] + p[i] + p[j + 1] - 2 * p[mid + 1];
}
for (int i = 0; i < n; i++) f[i][0] = 0x3f3f3f3f;
for (int i = 1; i < n; i++)
for (int j = 1; j <= m; j++) {
f[i][j] = 0x3f3f3f3f;
for (int k = 0; k <= i; k++) {
int t = cost[k][i];
if (k) t += f[k - 1][j - 1];
f[i][j] = min(f[i][j], t);
}
}
return f[n - 1][m];
}
};
时间复杂度 O ( n 2 m ) O(n^2m) O(n2m),空间 O ( n 2 ) O(n^2) O(n2)。