Post Office
Problem Statement There are V villages on a line at sorted positions x_1 < x_2 < < x_V. Place P post offices at village locations to minimize the total distance from each village to its nearest post office: _ i=1 ^ V...
Problem Statement
Rendered from the "Problem Statement" section in the LaTeX write-up.
There are $V$ villages on a line at sorted positions $x_1 < x_2 < \cdots < x_V$. Place $P$ post offices at village locations to minimize the total distance from each village to its nearest post office: \[ \min \sum_{i=1}^{V} \min_{j \in \text{post offices}} |x_i - x_j|. \]
Constraints: $1 \le P \le V \le 300$.
Editorial
The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.
Solution Approach
Precomputation: Cost of Serving a Range
For a contiguous group of villages $[i, j]$ served by a single optimally-placed post office, the optimal location is the median village $m = \lfloor(i+j)/2\rfloor$.
Define: \[ w[i][j] = \sum_{k=i}^{j} |x_k - x_m|, \quad m = \lfloor(i+j)/2\rfloor. \]
Proof (Proof of the recurrence $w[i][j] = w[i][j{-}1] + x_j - x_m$).
When extending from $[i, j{-}1]$ to $[i, j]$, the new median is $m' = \lfloor(i+j)/2\rfloor$. If $j - i$ is even, then $m' = m + 1$ where $m = \lfloor(i+j-1)/2\rfloor$ was the previous median. If $j - i$ is odd, then $m' = m$. In either case, the incremental cost formula $w[i][j] = w[i][j{-}1] + x_j - x_{m'}$ holds. This can be verified by noting that moving the median from $m$ to $m'$ changes costs symmetrically for the elements that switch sides, and $x_j - x_{m'}$ is the cost for the new village.
We compute $w[i][j]$ incrementally in $O(V^2)$ total time.
DP Formulation
Let $\mathrm{dp}[i][j]$ = minimum total distance for villages $1, \ldots, i$ using $j$ post offices.
Base case: $\mathrm{dp}[i][1] = w[1][i]$.
Transition: \[ \mathrm{dp}[i][j] = \min_{k = j-1}^{i-1} \bigl(\mathrm{dp}[k][j{-}1] + w[k{+}1][i]\bigr). \]
Answer: $\mathrm{dp}[V][P]$.
Reconstruction
Store the optimal split point $k$ in an auxiliary array $\mathrm{opt}[i][j]$. Trace backward to identify which group of villages each post office serves, placing each post office at the median of its group.
C++ Solution
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int V, P;
scanf("%d %d", &V, &P);
vector<int> x(V + 1);
for (int i = 1; i <= V; i++)
scanf("%d", &x[i]);
// Precompute w[i][j] = cost of one post office serving villages i..j
vector<vector<int>> w(V + 1, vector<int>(V + 1, 0));
for (int i = 1; i <= V; i++) {
for (int j = i + 1; j <= V; j++) {
int med = (i + j) / 2;
w[i][j] = w[i][j - 1] + x[j] - x[med];
}
}
// DP
const int INF = 1000000000;
vector<vector<int>> dp(V + 1, vector<int>(P + 1, INF));
vector<vector<int>> opt(V + 1, vector<int>(P + 1, 0));
for (int i = 1; i <= V; i++)
dp[i][1] = w[1][i];
for (int j = 2; j <= P; j++) {
for (int i = j; i <= V; i++) {
for (int k = j - 1; k <= i - 1; k++) {
int cost = dp[k][j - 1] + w[k + 1][i];
if (cost < dp[i][j]) {
dp[i][j] = cost;
opt[i][j] = k;
}
}
}
}
printf("%d\n", dp[V][P]);
// Reconstruct post office positions
vector<int> postOffices;
int curV = V, curP = P;
while (curP > 1) {
int k = opt[curV][curP];
int med = (k + 1 + curV) / 2;
postOffices.push_back(x[med]);
curV = k;
curP--;
}
int med = (1 + curV) / 2;
postOffices.push_back(x[med]);
reverse(postOffices.begin(), postOffices.end());
for (int i = 0; i < (int)postOffices.size(); i++)
printf("%d%c", postOffices[i], i + 1 < (int)postOffices.size() ? ' ' : '\n');
return 0;
}
Complexity Analysis
Time complexity: $O(V^2 \cdot P)$ for the DP, plus $O(V^2)$ for precomputing $w$. With $V, P \le 300$: at most $300^2 \times 300 = 27{,}000{,}000$ operations.
Space complexity: $O(V^2 + V \cdot P)$.
Possible Optimization
The optimal split point $\mathrm{opt}[i][j]$ is non-decreasing in $i$ for fixed $j$ (the ``divide and conquer'' or ``Knuth'' optimization). This reduces the DP to $O(V \cdot P)$ amortized. However, $O(V^2 P)$ is sufficient for $V \le 300$.
Code
C++ solution used for this page.
// IOI 2000 - Post Office
// Place P post offices among V villages on a line to minimize total distance.
// Each village connects to its nearest post office.
// Approach: DP with precomputed cost w[i][j] for serving villages i..j
// with one optimally-placed (median) post office.
// Complexity: O(V^2 * P) time, O(V^2 + V*P) space.
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int V, P;
cin >> V >> P;
vector<int> x(V + 1);
for (int i = 1; i <= V; i++) cin >> x[i];
// Precompute w[i][j] = cost of one post office serving villages i..j
// Optimal location is the median; w[i][j] = w[i][j-1] + x[j] - x[med]
vector<vector<int>> w(V + 1, vector<int>(V + 1, 0));
for (int i = 1; i <= V; i++) {
for (int j = i + 1; j <= V; j++) {
int med = (i + j) / 2;
w[i][j] = w[i][j - 1] + x[j] - x[med];
}
}
// dp[i][j] = min cost for villages 1..i with j post offices
const int INF = 1e9;
vector<vector<int>> dp(V + 1, vector<int>(P + 1, INF));
vector<vector<int>> opt(V + 1, vector<int>(P + 1, 0));
// Base case: one post office
for (int i = 1; i <= V; i++) {
dp[i][1] = w[1][i];
}
// Fill DP for 2..P post offices
for (int j = 2; j <= P; j++) {
for (int i = V; i >= j; i--) {
dp[i][j] = INF;
for (int k = j - 1; k <= i - 1; k++) {
int cost = dp[k][j - 1] + w[k + 1][i];
if (cost < dp[i][j]) {
dp[i][j] = cost;
opt[i][j] = k;
}
}
}
}
cout << dp[V][P] << "\n";
// Reconstruct post office positions (placed at medians of each group)
vector<int> postOffices;
int curV = V, curP = P;
while (curP > 1) {
int k = opt[curV][curP];
int med = (k + 1 + curV) / 2;
postOffices.push_back(x[med]);
curV = k;
curP--;
}
// Last group: villages 1..curV
int med = (1 + curV) / 2;
postOffices.push_back(x[med]);
reverse(postOffices.begin(), postOffices.end());
for (int i = 0; i < (int)postOffices.size(); i++) {
cout << postOffices[i] << (i + 1 < (int)postOffices.size() ? ' ' : '\n');
}
return 0;
}
Source Files and Assets
Raw files are still available here when you want the original TeX, C++, or statement assets.