一條直線公路上有 V (≤300) 個村莊,位置嚴格遞增。在其中 P (≤30, P≤V) 個村莊建郵局,使所有村莊到最近郵局的距離之和最小。輸出最小距離和。
對於連續區間 [i, j] 只建一個郵局,最優位置在中位數村莊(下標 mid = (i+j)/2)。最小距離和 cost[i][j] 可遞推計算:
cost[i][j] = cost[i][j-1] + pos[j] - pos[(i+j)/2]
證明:新增村莊 j,中位數可能右移,增量為 pos[j] - pos[mid]。
dp[k][j]:前 j 個村莊建 k 個郵局的最小距離和。轉移:
dp[k][j] = min_{i
初始化:dp[1][j] = cost[1][j]。答案 dp[P][V]。
O(P * V²),30 * 300² = 2.7e6,完全可行。O(P * V log V) 或 O(P * V),但非必要。#include
using namespace std;
const int INF = 0x3f3f3f3f;
int pos[305], cost[305][305], dp[35][305];
int main() {
int V, P;
while (cin >> V >> P) {
for (int i = 1; i <= V; ++i) cin >> pos[i];
for (int i = 1; i <= V; ++i) {
cost[i][i] = 0;
for (int j = i+1; j <= V; ++j)
cost[i][j] = cost[i][j-1] + pos[j] - pos[(i+j)/2];
}
memset(dp, 0x3f, sizeof(dp));
for (int j = 1; j <= V; ++j) dp[1][j] = cost[1][j];
for (int k = 2; k <= P; ++k)
for (int j = k; j <= V; ++j)
for (int i = k-1; i < j; ++i)
dp[k][j] = min(dp[k][j], dp[k-1][i] + cost[i+1][j]);
cout << dp[P][V] << '\n';
}
return 0;
}
O(V²)。dp[1][j] 是關鍵基礎情況。