IOI 1997
IOI 1997

Mars Explorer

Problem Statement A robot navigates an n n grid where some cells are obstacles. Starting at (1,1), it must reach (n,n), return to (1,1), and go to (n,n) again --- three traversals total. On forward trips the robot mov...

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

Problem Statement

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

A robot navigates an $n \times n$ grid where some cells are obstacles. Starting at $(1,1)$, it must reach $(n,n)$, return to $(1,1)$, and go to $(n,n)$ again --- three traversals total. On forward trips the robot moves only right or down; on the return trip it moves only left or up.

Maximize the number of distinct non-obstacle cells visited across all three traversals.

Constraints: $n \le 30$.

Editorial

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

Solution Approach

Reduction to Three Simultaneous Forward Paths

A return trip from $(n,n)$ to $(1,1)$ using left/up moves is equivalent to a forward trip from $(1,1)$ to $(n,n)$ using right/down moves (simply reverse the path). Thus the problem reduces to finding three forward paths from $(1,1)$ to $(n,n)$ that collectively visit the maximum number of distinct cells.

DP on Three Simultaneous Paths

We advance all three paths in lockstep. At step $t$ (where $0 \le t \le 2(n-1)$), each path occupies a cell $(r_i, c_i)$ with $r_i + c_i = t + 2$. Since $c_i$ is determined by $t$ and $r_i$, the state is $(t, r_1, r_2, r_3)$.

Value: Maximum distinct cells visited by positions $1, \ldots, t$.

Transition: Each path moves right ($r_i$ unchanged) or down ($r_i + 1$), giving $2^3 = 8$ combinations per state.

Symmetry Optimization

Without loss of generality, enforce $r_1 \le r_2 \le r_3$. This reduces the number of states by a factor of up to 6.

Counting Distinct Cells

At each step, the three paths occupy cells $(r_1, c_1)$, $(r_2, c_2)$, $(r_3, c_3)$. Since they share the same $t$, distinct cells correspond to distinct $r$-values: positions with the same $r_i$ also share $c_i$. The number of new distinct cells is $|\{r_1, r_2, r_3\}|$ (provided all cells are non-obstacles).

C++ Solution

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n;
int grid[32][32]; // 1 = free, 0 = obstacle
int dp[62][32][32][32]; // dp[t][r1][r2][r3]

// Returns number of distinct free cells, or -1 if any cell is invalid
int countDistinct(int t, int r1, int r2, int r3) {
    int c1 = t - r1 + 2, c2 = t - r2 + 2, c3 = t - r3 + 2;
    if (r1 < 1 || r1 > n || c1 < 1 || c1 > n || !grid[r1][c1]) return -1;
    if (r2 < 1 || r2 > n || c2 < 1 || c2 > n || !grid[r2][c2]) return -1;
    if (r3 < 1 || r3 > n || c3 < 1 || c3 > n || !grid[r3][c3]) return -1;

    int cnt = 1;
    if (r2 != r1) cnt++;
    if (r3 != r1 && r3 != r2) cnt++;
    return cnt;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            scanf("%d", &grid[i][j]);

    memset(dp, -1, sizeof(dp));

    int maxT = 2 * (n - 1);

    // Base case: t = 0, all three paths at (1,1)
    if (grid[1][1])
        dp[0][1][1][1] = 1;

    for (int t = 0; t < maxT; t++) {
        for (int r1 = 1; r1 <= n; r1++) {
            int c1 = t - r1 + 2;
            if (c1 < 1 || c1 > n) continue;
            for (int r2 = r1; r2 <= n; r2++) {
                int c2 = t - r2 + 2;
                if (c2 < 1 || c2 > n) continue;
                for (int r3 = r2; r3 <= n; r3++) {
                    int c3 = t - r3 + 2;
                    if (c3 < 1 || c3 > n) continue;
                    if (dp[t][r1][r2][r3] < 0) continue;

                    int cur = dp[t][r1][r2][r3];

                    // Try all 8 move combinations (right or down for each path)
                    for (int d1 = 0; d1 <= 1; d1++)
                    for (int d2 = 0; d2 <= 1; d2++)
                    for (int d3 = 0; d3 <= 1; d3++) {
                        int nr1 = r1 + d1, nr2 = r2 + d2, nr3 = r3 + d3;

                        // Sort to maintain r1 <= r2 <= r3 invariant
                        int sr[3] = {nr1, nr2, nr3};
                        sort(sr, sr + 3);

                        int add = countDistinct(t + 1, sr[0], sr[1], sr[2]);
                        if (add < 0) continue;

                        int& ref = dp[t+1][sr[0]][sr[1]][sr[2]];
                        ref = max(ref, cur + add);
                    }
                }
            }
        }
    }

    printf("%d\n", dp[maxT][n][n][n]);
    return 0;
}

Complexity Analysis

  • Time complexity: $O(n^4)$. There are $2(n-1) + 1$ time steps and $O(n^3/6)$ ordered triples per step (after the symmetry reduction). Each state tries 8 transitions. Total: $O(n \cdot n^3 / 6 \cdot 8) = O(n^4)$. For $n = 30$: roughly $30 \cdot 30^3 / 6 \cdot 8 \approx 1{,}080{,}000$ operations.

  • Space complexity: $O(n^4)$ for the DP table. For $n = 30$: $60 \times 30^3 \approx 1{,}620{,}000$ entries, each a 4-byte integer.

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 1997 - Mars Explorer
// 3 simultaneous paths from (1,1) to (n,n), maximize distinct cells visited
// DP on (step, r1, r2, r3) with r1 <= r2 <= r3 symmetry optimization
// Time: O(n^4), Space: O(n^4)
#include <bits/stdc++.h>
using namespace std;

int n;
int grid[32][32]; // 1 = free, 0 = obstacle
int dp[62][32][32][32];

// Count distinct valid cells at step t for rows r1 <= r2 <= r3
// Returns -1 if any cell is invalid
int countDistinct(int t, int r1, int r2, int r3) {
    int c1 = t - r1 + 2, c2 = t - r2 + 2, c3 = t - r3 + 2;
    if (r1 < 1 || r1 > n || c1 < 1 || c1 > n || !grid[r1][c1]) return -1;
    if (r2 < 1 || r2 > n || c2 < 1 || c2 > n || !grid[r2][c2]) return -1;
    if (r3 < 1 || r3 > n || c3 < 1 || c3 > n || !grid[r3][c3]) return -1;

    int cnt = 1;
    if (r2 != r1) cnt++;
    if (r3 != r1 && r3 != r2) cnt++;
    return cnt;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            scanf("%d", &grid[i][j]);

    memset(dp, -1, sizeof(dp));

    int maxT = 2 * (n - 1);

    // Base: t=0, all paths at (1,1)
    if (grid[1][1])
        dp[0][1][1][1] = 1;

    for (int t = 0; t < maxT; t++) {
        for (int r1 = 1; r1 <= n; r1++) {
            int c1 = t - r1 + 2;
            if (c1 < 1 || c1 > n) continue;
            for (int r2 = r1; r2 <= n; r2++) {
                int c2 = t - r2 + 2;
                if (c2 < 1 || c2 > n) continue;
                for (int r3 = r2; r3 <= n; r3++) {
                    int c3 = t - r3 + 2;
                    if (c3 < 1 || c3 > n) continue;
                    if (dp[t][r1][r2][r3] < 0) continue;

                    int cur = dp[t][r1][r2][r3];

                    // Each path moves right (r stays) or down (r+1)
                    for (int d1 = 0; d1 <= 1; d1++)
                    for (int d2 = 0; d2 <= 1; d2++)
                    for (int d3 = 0; d3 <= 1; d3++) {
                        int nr[3] = {r1 + d1, r2 + d2, r3 + d3};
                        sort(nr, nr + 3); // enforce ordering

                        int add = countDistinct(t + 1, nr[0], nr[1], nr[2]);
                        if (add < 0) continue;

                        int& ref = dp[t + 1][nr[0]][nr[1]][nr[2]];
                        ref = max(ref, cur + add);
                    }
                }
            }
        }
    }

    printf("%d\n", dp[maxT][n][n][n]);
    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