Magic Squares
Problem Statement A 2 4 grid contains a permutation of the numbers 1 through 8, arranged in a cycle: 1 2 3 4 8 7 6 5 We store this linearly as positions s[0..7], where s[0..3] is the top row (left to right) and s[4..7...
Problem Statement
Rendered from the "Problem Statement" section in the LaTeX write-up.
A $2 \times 4$ grid contains a permutation of the numbers 1 through 8, arranged in a cycle:
1 2 3 4
8 7 6 5
We store this linearly as positions $s[0..7]$, where $s[0..3]$ is the top row (left to right) and $s[4..7]$ is the bottom row (left to right). In the initial configuration, the bottom row reads $8, 7, 6, 5$ left to right.
Three operations are defined:
A -- Row swap: Exchange the top and bottom rows. {\small $
\begin{pmatrix}s_0 & s_1 & s_2 & s_3 \\ s_4 & s_5 & s_6 & s_7\end{pmatrix}\to
\begin{pmatrix}s_4 & s_5 & s_6 & s_7 \\ s_0 & s_1 & s_2 & s_3\end{pmatrix}$}
B -- Circular right shift: Shift each row one position to the right (the rightmost element wraps to the left). {\small $
\begin{pmatrix}s_0 & s_1 & s_2 & s_3 \\ s_4 & s_5 & s_6 & s_7\end{pmatrix}\to
\begin{pmatrix}s_3 & s_0 & s_1 & s_2 \\ s_7 & s_4 & s_5 & s_6\end{pmatrix}$}
C -- Center rotation: Rotate the inner $2 \times 2$ block (positions $s_1, s_2, s_5, s_6$) clockwise by 90 degrees. {\small $
\begin{pmatrix}s_0 & s_1 & s_2 & s_3 \\ s_4 & s_5 & s_6 & s_7\end{pmatrix}\to
\begin{pmatrix}s_0 & s_5 & s_1 & s_3 \\ s_4 & s_6 & s_2 & s_7\end{pmatrix}$}
Given a target configuration, find the shortest sequence of operations to transform the initial state into the target.
Editorial
The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.
Solution Approach
BFS on Permutations
The state space consists of all permutations of $\{1,\ldots,8\}$, totaling $8! = 40{,}320$ states. This is small enough for breadth-first search, which finds the shortest path.
Represent each state as an array of 8 elements.
Encode each state as a unique integer for hashing (e.g., treat the digits as a base-10 number).
Start BFS from the initial state.
At each state, apply all three operations A, B, C to generate neighbors.
When the target is reached, reconstruct the path by following parent pointers.
Operation A -- Correctness
Operation A swaps the two rows. In our linear representation, this means: \[ (s_0, s_1, s_2, s_3, s_4, s_5, s_6, s_7) \;\to\; (s_4, s_5, s_6, s_7, s_0, s_1, s_2, s_3). \] Note that this is a direct row swap, not a reversal. The original code had a bug where opA performed $r[i] = s[7-i]$ and $r[7-i] = s[i]$, which reverses each row rather than swapping them. The corrected version below simply exchanges positions $0 \leftrightarrow 4$, $1 \leftrightarrow 5$, $2 \leftrightarrow 6$, $3 \leftrightarrow 7$.
C++ Solution
#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;
struct State {
int s[8];
};
ll encode(const State& st) {
ll v = 0;
for (int i = 0; i < 8; i++)
v = v * 10 + st.s[i];
return v;
}
// Operation A: swap top and bottom rows
State opA(const State& st) {
State r;
for (int i = 0; i < 4; i++) {
r.s[i] = st.s[i + 4];
r.s[i + 4] = st.s[i];
}
return r;
}
// Operation B: circular right shift each row
State opB(const State& st) {
State r;
r.s[0] = st.s[3]; r.s[1] = st.s[0]; r.s[2] = st.s[1]; r.s[3] = st.s[2];
r.s[4] = st.s[7]; r.s[5] = st.s[4]; r.s[6] = st.s[5]; r.s[7] = st.s[6];
return r;
}
// Operation C: clockwise rotation of center 2x2 block
// Center block positions: s[1], s[2] (top), s[5], s[6] (bottom)
// Clockwise: top-left <- bottom-left, top-right <- top-left,
// bottom-right <- top-right, bottom-left <- bottom-right
// i.e., s[1] <- s[5], s[2] <- s[1], s[6] <- s[2], s[5] <- s[6]
State opC(const State& st) {
State r = st;
r.s[1] = st.s[5];
r.s[2] = st.s[1];
r.s[6] = st.s[2];
r.s[5] = st.s[6];
return r;
}
int main() {
// Initial state: 1 2 3 4 / 8 7 6 5
State init;
init.s[0]=1; init.s[1]=2; init.s[2]=3; init.s[3]=4;
init.s[4]=8; init.s[5]=7; init.s[6]=6; init.s[7]=5;
// Read target
State target;
for (int i = 0; i < 8; i++)
scanf("%d", &target.s[i]);
ll initCode = encode(init);
ll targetCode = encode(target);
if (initCode == targetCode) {
printf("0\n\n");
return 0;
}
// BFS
map<ll, pair<ll, char>> parent;
map<ll, bool> visited;
queue<State> q;
q.push(init);
visited[initCode] = true;
while (!q.empty()) {
State cur = q.front(); q.pop();
ll curCode = encode(cur);
State nexts[3] = {opA(cur), opB(cur), opC(cur)};
char ops[3] = {'A', 'B', 'C'};
for (int i = 0; i < 3; i++) {
ll nc = encode(nexts[i]);
if (!visited[nc]) {
visited[nc] = true;
parent[nc] = {curCode, ops[i]};
if (nc == targetCode) {
// Reconstruct path
string path;
ll c = targetCode;
while (c != initCode) {
path += parent[c].second;
c = parent[c].first;
}
reverse(path.begin(), path.end());
printf("%d\n%s\n", (int)path.size(), path.c_str());
return 0;
}
q.push(nexts[i]);
}
}
}
return 0;
}
Complexity Analysis
Time complexity: $O(8!)$. We visit at most $8! = 40{,}320$ states, applying 3 operations per state. The
mapoperations add an $O(\log(8!))$ factor, giving $O(8! \cdot \log(8!)) \approx 640{,}000$ operations in total.Space complexity: $O(8!)$ for the visited map and parent pointers.
Code
C++ solution used for this page.
// IOI 1996 - Magic Squares
// BFS on permutations of 2x4 grid with 3 operations (A, B, C)
// State space: 8! = 40320, Time: O(8! * 3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// State: 8 positions
// Top row left-to-right: s[0..3], Bottom row left-to-right: s[4..7]
// Problem layout: 1 2 3 4 (top)
// 8 7 6 5 (bottom, read right-to-left in problem)
struct State {
int s[8];
};
ll encode(const State& st) {
ll v = 0;
for (int i = 0; i < 8; i++)
v = v * 10 + st.s[i];
return v;
}
// Operation A: swap top and bottom rows
// Top becomes bottom, bottom becomes top
State opA(const State& st) {
State r;
r.s[0] = st.s[4]; r.s[1] = st.s[5]; r.s[2] = st.s[6]; r.s[3] = st.s[7];
r.s[4] = st.s[0]; r.s[5] = st.s[1]; r.s[6] = st.s[2]; r.s[7] = st.s[3];
return r;
}
// Operation B: circular right shift each row
State opB(const State& st) {
State r;
r.s[0] = st.s[3]; r.s[1] = st.s[0]; r.s[2] = st.s[1]; r.s[3] = st.s[2];
r.s[4] = st.s[7]; r.s[5] = st.s[4]; r.s[6] = st.s[5]; r.s[7] = st.s[6];
return r;
}
// Operation C: clockwise rotation of center 2x2 block
// Center block positions: s[1], s[2] (top), s[5], s[6] (bottom)
// Clockwise: top-left <- bottom-left, top-right <- top-left,
// bottom-right <- top-right, bottom-left <- bottom-right
State opC(const State& st) {
State r = st;
r.s[1] = st.s[5];
r.s[2] = st.s[1];
r.s[6] = st.s[2];
r.s[5] = st.s[6];
return r;
}
int main() {
// Initial state: 1 2 3 4 / 8 7 6 5
State init;
init.s[0] = 1; init.s[1] = 2; init.s[2] = 3; init.s[3] = 4;
init.s[4] = 8; init.s[5] = 7; init.s[6] = 6; init.s[7] = 5;
// Read target
State target;
for (int i = 0; i < 8; i++)
scanf("%d", &target.s[i]);
ll initCode = encode(init);
ll targetCode = encode(target);
if (initCode == targetCode) {
printf("0\n\n");
return 0;
}
// BFS
unordered_map<ll, pair<ll, char>> parent;
unordered_map<ll, bool> visited;
queue<State> q;
q.push(init);
visited[initCode] = true;
while (!q.empty()) {
State cur = q.front(); q.pop();
ll curCode = encode(cur);
State nexts[3] = {opA(cur), opB(cur), opC(cur)};
char ops[3] = {'A', 'B', 'C'};
for (int i = 0; i < 3; i++) {
ll nc = encode(nexts[i]);
if (!visited[nc]) {
visited[nc] = true;
parent[nc] = {curCode, ops[i]};
if (nc == targetCode) {
// Reconstruct path
string path;
ll c = targetCode;
while (c != initCode) {
path += parent[c].second;
c = parent[c].first;
}
reverse(path.begin(), path.end());
printf("%d\n%s\n", (int)path.size(), path.c_str());
return 0;
}
q.push(nexts[i]);
}
}
}
return 0;
}
Source Files and Assets
Raw files are still available here when you want the original TeX, C++, or statement assets.