IOI 1994
IOI 1994

The Clock

Problem Statement There are 9 clocks arranged in a 3 3 grid. Each clock points to 12, 3, 6, or 9 (represented as 0, 1, 2, 3 respectively, where 0 means 12 o'clock). There are 9 possible moves, each of which rotates a...

Updated May 21, 2026
Track IOI
Year 1994
Statement Rendered from TeX
TeXC++Rendered statement

Problem Statement

Rendered from the "Problem Statement" section in the LaTeX write-up.

There are 9 clocks arranged in a $3 \times 3$ grid. Each clock points to 12, 3, 6, or 9 (represented as 0, 1, 2, 3 respectively, where 0 means 12 o'clock). There are 9 possible moves, each of which rotates a specific subset of clocks by 90 degrees clockwise. The goal is to make all clocks point to 12 (all zeros) using the minimum number of moves.

The 9 moves affect the following clocks (numbered 1--9 in row-major order):

  • Move 1: clocks 1, 2, 4, 5

  • Move 2: clocks 1, 2, 3

  • Move 3: clocks 2, 3, 5, 6

  • Move 4: clocks 1, 4, 7

  • Move 5: clocks 2, 4, 5, 6, 8

  • Move 6: clocks 3, 6, 9

  • Move 7: clocks 4, 5, 7, 8

  • Move 8: clocks 7, 8, 9

  • Move 9: clocks 5, 6, 8, 9

Editorial

The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.

Solution Approach

Key Observation

Since each clock has only 4 states (mod 4), and each move applied 4 times returns all affected clocks to their original positions, each move needs to be applied at most 3 times. This means the total state space is $4^9 = 262{,}144$ states, which is small enough for BFS.

Algorithm: BFS

  1. Encode the 9 clock values as a single state (e.g., treating each clock as a base-4 digit).

  2. Starting from the initial state, perform BFS.

  3. Each state has 9 neighbors (one per move).

  4. The target state is $0$ (all clocks at 12).

  5. BFS guarantees the shortest sequence of moves.

Alternative: Brute Force with 4 Loops

Since each of the 9 moves is applied 0--3 times, we can enumerate all $4^9$ combinations. This is feasible and simpler to implement.

C++ Solution

#include <cstdio>
#include <cstring>
#include <queue>
#include <map>
using namespace std;

// Clocks affected by each move (0-indexed)
int moves[9][5] = {
    {0, 1, 3, 4, -1},   // Move 1: clocks 1,2,4,5
    {0, 1, 2, -1, -1},  // Move 2: clocks 1,2,3
    {1, 2, 4, 5, -1},   // Move 3: clocks 2,3,5,6
    {0, 3, 6, -1, -1},  // Move 4: clocks 1,4,7
    {1, 3, 4, 5, 7},    // Move 5: clocks 2,4,5,6,8
    {2, 5, 8, -1, -1},  // Move 6: clocks 3,6,9
    {3, 4, 6, 7, -1},   // Move 7: clocks 4,5,7,8
    {6, 7, 8, -1, -1},  // Move 8: clocks 7,8,9
    {4, 5, 7, 8, -1}    // Move 9: clocks 5,6,8,9
};

int clk[9];

int encode(int c[]) {
    int s = 0;
    for (int i = 0; i < 9; i++)
        s = s * 4 + c[i];
    return s;
}

void decode(int s, int c[]) {
    for (int i = 8; i >= 0; i--) {
        c[i] = s % 4;
        s /= 4;
    }
}

void applyMove(int c[], int m) {
    for (int k = 0; k < 5 && moves[m][k] != -1; k++)
        c[moves[m][k]] = (c[moves[m][k]] + 1) % 4;
}

int main() {
    for (int i = 0; i < 9; i++) {
        int x;
        scanf("%d", &x);
        // Convert: 12->0, 3->1, 6->2, 9->3
        clk[i] = (x / 3) % 4;
    }

    // BFS
    int start = encode(clk);
    int target = 0; // all zeros

    if (start == target) {
        printf("0\n");
        return 0;
    }

    map<int, int> dist;
    map<int, pair<int, int>> parent; // state -> (prev_state, move)
    queue<int> q;
    q.push(start);
    dist[start] = 0;

    while (!q.empty()) {
        int u = q.front(); q.pop();
        int d = dist[u];

        for (int m = 0; m < 9; m++) {
            int tmp[9];
            decode(u, tmp);
            applyMove(tmp, m);
            int v = encode(tmp);
            if (dist.find(v) == dist.end()) {
                dist[v] = d + 1;
                parent[v] = {u, m};
                if (v == target) {
                    // Reconstruct path
                    int path[1000], plen = 0;
                    int cur = target;
                    while (cur != start) {
                        path[plen++] = parent[cur].second + 1;
                        cur = parent[cur].first;
                    }
                    for (int i = plen - 1; i >= 0; i--) {
                        if (i < plen - 1) printf(" ");
                        printf("%d", path[i]);
                    }
                    printf("\n");
                    return 0;
                }
                q.push(v);
            }
        }
    }

    return 0;
}

Complexity Analysis

  • Time complexity: $O(4^9 \times 9) = O(2{,}359{,}296)$. There are at most $4^9 = 262{,}144$ distinct states, and from each state we try 9 moves. This is very fast in practice.

  • Space complexity: $O(4^9)$ for the BFS visited set and parent map.

Code

C++ solution used for this page.

C++

Clean code view with a raw-file link when you want the original source.

Raw file
// IOI 1994 - The Clock
// BFS over all 4^9 states to find shortest move sequence
// Each of 9 moves rotates a subset of clocks 90 degrees clockwise
// Time: O(4^9 * 9), Space: O(4^9)
#include <bits/stdc++.h>
using namespace std;

// Clocks affected by each move (0-indexed clocks)
const int moves[9][5] = {
    {0, 1, 3, 4, -1},   // Move 1: clocks 1,2,4,5
    {0, 1, 2, -1, -1},  // Move 2: clocks 1,2,3
    {1, 2, 4, 5, -1},   // Move 3: clocks 2,3,5,6
    {0, 3, 6, -1, -1},  // Move 4: clocks 1,4,7
    {1, 3, 4, 5, 7},    // Move 5: clocks 2,4,5,6,8
    {2, 5, 8, -1, -1},  // Move 6: clocks 3,6,9
    {3, 4, 6, 7, -1},   // Move 7: clocks 4,5,7,8
    {6, 7, 8, -1, -1},  // Move 8: clocks 7,8,9
    {4, 5, 7, 8, -1}    // Move 9: clocks 5,6,8,9
};

int encode(int c[]) {
    int s = 0;
    for (int i = 0; i < 9; i++)
        s = s * 4 + c[i];
    return s;
}

void decode(int s, int c[]) {
    for (int i = 8; i >= 0; i--) {
        c[i] = s % 4;
        s /= 4;
    }
}

void applyMove(int c[], int m) {
    for (int j = 0; j < 5 && moves[m][j] != -1; j++)
        c[moves[m][j]] = (c[moves[m][j]] + 1) % 4;
}

int main() {
    int clk[9];
    for (int i = 0; i < 9; i++) {
        int x;
        scanf("%d", &x);
        // Input: 12, 3, 6, or 9 (hours)
        // Convert to 0,1,2,3: 12->0, 3->1, 6->2, 9->3
        clk[i] = x % 12 / 3;
    }

    int start = encode(clk);
    int target = 0; // all clocks at 12

    if (start == target) {
        printf("\n");
        return 0;
    }

    // BFS
    unordered_map<int, int> dist;
    unordered_map<int, pair<int, int>> parent; // state -> (prev_state, move)
    queue<int> q;
    q.push(start);
    dist[start] = 0;

    while (!q.empty()) {
        int u = q.front(); q.pop();

        for (int m = 0; m < 9; m++) {
            int tmp[9];
            decode(u, tmp);
            applyMove(tmp, m);
            int v = encode(tmp);
            if (dist.find(v) == dist.end()) {
                dist[v] = dist[u] + 1;
                parent[v] = {u, m};
                if (v == target) {
                    // Reconstruct path
                    vector<int> path;
                    int cur = target;
                    while (cur != start) {
                        path.push_back(parent[cur].second + 1);
                        cur = parent[cur].first;
                    }
                    reverse(path.begin(), path.end());
                    for (int i = 0; i < (int)path.size(); i++) {
                        if (i) printf(" ");
                        printf("%d", path[i]);
                    }
                    printf("\n");
                    return 0;
                }
                q.push(v);
            }
        }
    }

    return 0;
}

Source Files and Assets

Raw files are still available here when you want the original TeX, C++, or statement assets.

Show raw files