Camelot
Problem Statement On an R C chessboard (R 30, C 26), there is one king and up to 63 knights. All pieces must gather at a single square. A knight may optionally pick up the king en route: it visits the king's square, c...
Problem Statement
Rendered from the "Problem Statement" section in the LaTeX write-up.
On an $R \times C$ chessboard ($R \le 30$, $C \le 26$), there is one king and up to 63 knights. All pieces must gather at a single square. A knight may optionally pick up the king en route: it visits the king's square, carries the king, and continues to the gathering point. The king walks using Chebyshev distance (one step in any of 8 directions per move).
Find the minimum total number of moves for all pieces to gather.
Editorial
The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.
Solution Approach
BFS for Knight Distances
Precompute the shortest knight distance between every pair of squares using BFS from each square. Let $\mathrm{kdist}[s][t]$ denote this distance. The board has at most $S = 30 \times 26 = 780$ squares.
King Distance
The king's distance is the Chebyshev distance: \[ \mathrm{kingDist}((r_k, c_k), (r, c)) = \max(|r_k - r|, |c_k - c|). \]
Enumeration
For each potential gathering square $g$, compute:
Option 1 -- King walks alone.
Option 2 -- A knight picks up the king.
Knight $i$ detours through pickup square $p$, where the king walks to $p$: \[ \mathrm{cost}_2 = \sum_{j \ne i} \mathrm{kdist}[\mathrm{knight}_j][g] + \mathrm{kdist}[\mathrm{knight}_i][p] + \mathrm{kdist}[p][g] + \mathrm{kingDist}(\mathrm{king}, p). \]
Take the minimum over all $g$, all knights $i$, and all pickup points $p$.
Pickup Point Search
To guarantee correctness, we search all squares as potential pickup points. Although this increases the time to $O(S^2 \cdot n)$, it remains feasible for the given constraints ($S \le 780$, $n \le 63$).
C++ Solution
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <climits>
using namespace std;
const int MAXR = 30, MAXC = 26;
int R, C;
int kdist[MAXR * MAXC][MAXR * MAXC];
int dx[] = {-2, -2, -1, -1, 1, 1, 2, 2};
int dy[] = {-1, 1, -2, 2, -2, 2, -1, 1};
int idx(int r, int c) { return r * C + c; }
void bfs(int src) {
queue<int> q;
q.push(src);
kdist[src][src] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
int r = u / C, c = u % C;
for (int d = 0; d < 8; d++) {
int nr = r + dx[d], nc = c + dy[d];
if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue;
int v = idx(nr, nc);
if (kdist[src][v] == -1) {
kdist[src][v] = kdist[src][u] + 1;
q.push(v);
}
}
}
}
int main() {
scanf("%d %d", &R, &C);
int total = R * C;
// Read king position
char col;
int row;
scanf(" %c %d", &col, &row);
int kingR = row - 1, kingC = col - 'A';
// Read knight positions
int knights[65], numKnights = 0;
while (scanf(" %c %d", &col, &row) == 2) {
int kr = row - 1, kc = col - 'A';
knights[numKnights++] = idx(kr, kc);
}
// BFS from every square
memset(kdist, -1, sizeof(kdist));
for (int i = 0; i < total; i++)
bfs(i);
// Replace unreachable entries with a large sentinel
const int INF = 1000000;
for (int i = 0; i < total; i++)
for (int j = 0; j < total; j++)
if (kdist[i][j] == -1)
kdist[i][j] = INF;
// Handle edge case: no knights
if (numKnights == 0) {
printf("0\n");
return 0;
}
int ans = INT_MAX;
// Try each gathering point
for (int g = 0; g < total; g++) {
int gr = g / C, gc = g % C;
// Sum of all knight distances to g
int base = 0;
bool reachable = true;
for (int i = 0; i < numKnights; i++) {
if (kdist[knights[i]][g] >= INF) { reachable = false; break; }
base += kdist[knights[i]][g];
}
if (!reachable) continue;
// Option 1: king walks alone to g
int kingAlone = base + max(abs(kingR - gr), abs(kingC - gc));
ans = min(ans, kingAlone);
// Option 2: one knight picks up king at some pickup point p
for (int i = 0; i < numKnights; i++) {
int saved = kdist[knights[i]][g];
for (int p = 0; p < total; p++) {
int pr = p / C, pc = p % C;
int kingWalk = max(abs(kingR - pr), abs(kingC - pc));
int knightDetour = kdist[knights[i]][p] + kdist[p][g];
if (knightDetour >= INF) continue;
int cost = base - saved + knightDetour + kingWalk;
ans = min(ans, cost);
}
}
}
printf("%d\n", ans);
return 0;
}
Complexity Analysis
Time complexity:
BFS from every square: $O(S^2)$ where $S = R \times C \le 780$.
Main enumeration: for each gathering point ($O(S)$), for each knight ($O(n)$), for each pickup point ($O(S)$): $O(S^2 \cdot n)$.
Total: $O(S^2 \cdot n) \approx 780^2 \times 63 \approx 38{,}000{,}000$.
Space complexity: $O(S^2)$ for the knight distance table ($\approx 2.4$ MB).
Code
C++ solution used for this page.
// IOI 1998 - Camelot
// BFS knight distances + enumerate gathering point and pickup point for king
// Time: O(S^2 + S^2 * n), Space: O(S^2) where S = R*C
#include <bits/stdc++.h>
using namespace std;
const int MAXR = 30, MAXC = 26;
const int INF = 1000000;
int R, C;
int kdist[MAXR * MAXC][MAXR * MAXC];
int dx[] = {-2, -2, -1, -1, 1, 1, 2, 2};
int dy[] = {-1, 1, -2, 2, -2, 2, -1, 1};
int idx(int r, int c) { return r * C + c; }
void bfs(int src) {
queue<int> q;
q.push(src);
kdist[src][src] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
int r = u / C, c = u % C;
for (int d = 0; d < 8; d++) {
int nr = r + dx[d], nc = c + dy[d];
if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue;
int v = idx(nr, nc);
if (kdist[src][v] == -1) {
kdist[src][v] = kdist[src][u] + 1;
q.push(v);
}
}
}
}
int main() {
scanf("%d %d", &R, &C);
int total = R * C;
// Read king position
char col;
int row;
scanf(" %c %d", &col, &row);
int kingR = row - 1, kingC = col - 'A';
// Read knight positions
vector<int> knights;
while (scanf(" %c %d", &col, &row) == 2) {
int kr = row - 1, kc = col - 'A';
knights.push_back(idx(kr, kc));
}
int numKnights = (int)knights.size();
// Handle no knights: king stays put
if (numKnights == 0) {
printf("0\n");
return 0;
}
// BFS from every square for knight distances
memset(kdist, -1, sizeof(kdist));
for (int i = 0; i < total; i++)
bfs(i);
// Replace unreachable with large value
for (int i = 0; i < total; i++)
for (int j = 0; j < total; j++)
if (kdist[i][j] == -1)
kdist[i][j] = INF;
int ans = INT_MAX;
// Try each gathering point
for (int g = 0; g < total; g++) {
// Sum of knight distances to g
int base = 0;
bool reachable = true;
for (int i = 0; i < numKnights; i++) {
if (kdist[knights[i]][g] >= INF) { reachable = false; break; }
base += kdist[knights[i]][g];
}
if (!reachable) continue;
int gr = g / C, gc = g % C;
// Option 1: king walks alone to gathering point
int kingAlone = base + max(abs(kingR - gr), abs(kingC - gc));
ans = min(ans, kingAlone);
// Option 2: one knight picks up king at some point p
for (int i = 0; i < numKnights; i++) {
int saved = kdist[knights[i]][g];
// Try all squares as pickup points
for (int p = 0; p < total; p++) {
int pr = p / C, pc = p % C;
int kingWalk = max(abs(kingR - pr), abs(kingC - pc));
int knightDetour = kdist[knights[i]][p] + kdist[p][g];
if (knightDetour >= INF) continue;
int cost = base - saved + knightDetour + kingWalk;
ans = min(ans, cost);
}
}
}
printf("%d\n", ans);
return 0;
}
Source Files and Assets
Raw files are still available here when you want the original TeX, C++, or statement assets.