All Euler problems
Project Euler

Convex Path in Square

Let F(N) denote the number of lattice paths from (0,0) to (N,N) using unit steps right (R) or up (U) such that the path is convex -- meaning the path never goes above the line y = x and then return...

Source sync Apr 19, 2026
Problem #0604
Level Level 21
Solved By 587
Languages C++, Python
Answer 1398582231101
Length 434 words
geometrydynamic_programmingsequence

Problem Statement

This archive keeps the full statement, math, and original media on the page.

Let \(F(N)\) be the maximum number of lattice points in an axis-aligned \(N\times N\) square that the graph of a single <strong>strictly convex</strong> increasing function can pass through.

You are given that \(F(1) = 2\), \(F(3) = 3\), \(F(9) = 6\), \(F(11) = 7\), \(F(100) = 30\) and \(F(50000) = 1898\).

Below is the graph of a function reaching the maximum \(3\) for \(N=3\):

PIC

Find \(F(10^{18})\).

Problem 604: Convex Path in Square

Mathematical Foundation

Definition. A convex lattice path from (0,0)(0,0) to (N,N)(N,N) is a monotone lattice path (using steps R=(1,0)R = (1,0) and U=(0,1)U = (0,1)) such that the sequence of direction changes forms a convex curve — equivalently, all RR steps come before all UU steps at each “level,” and the path’s slope is non-increasing.

Theorem 1 (Catalan Structure). The number of monotone lattice paths from (0,0)(0,0) to (N,N)(N,N) that stay weakly below the diagonal y=xy = x is the NN-th Catalan number:

CN=1N+1(2NN).C_N = \frac{1}{N+1}\binom{2N}{N}.

Proof. This is the classical ballot problem / Bertrand reflection argument. The total number of paths is (2NN)\binom{2N}{N}. The number of “bad” paths (those that cross above y=xy = x) can be placed in bijection with paths from (0,0)(0,0) to (N1,N+1)(N-1, N+1) by reflecting the portion after the first crossing point about y=x+1y = x + 1. Hence the number of bad paths is (2NN1)\binom{2N}{N-1}, and

CN=(2NN)(2NN1)=1N+1(2NN).C_N = \binom{2N}{N} - \binom{2N}{N-1} = \frac{1}{N+1}\binom{2N}{N}. \quad \square

Lemma 1 (Convex Path Decomposition). A convex lattice path is uniquely determined by its sequence of “run lengths” (r1,u1,r2,u2,)(r_1, u_1, r_2, u_2, \ldots) where the rir_i (right-runs) are non-increasing and the uiu_i (up-runs) are non-decreasing, subject to the convexity constraint on slopes.

Proof. The convexity condition requires that the slope of each segment is non-increasing. A segment from the ii-th turn to the (i+1)(i+1)-th turn has slope ui/riu_i / r_i. Convexity demands ui/riui+1/ri+1u_i/r_i \geq u_{i+1}/r_{i+1} (for the path bending appropriately). The decomposition is unique since runs are maximal. \square

Theorem 2 (Counting via Integer Partitions). The number of convex paths can be expressed as a sum over integer partitions of NN:

F(N)=λNp(λ)F(N) = \sum_{\lambda \vdash N} p(\lambda)

where p(λ)p(\lambda) accounts for the valid convex slope sequences consistent with partition λ\lambda.

Proof. Each convex path corresponds to a pair of compositions of NN (for horizontal and vertical runs) satisfying the slope monotonicity condition. By grouping these according to their sorted forms, each valid pair corresponds to a partition structure. The count follows by enumeration. \square

Editorial

Count convex lattice paths in a grid. We dynamic programming over run-length decompositions. Finally, state: (remaining_horizontal, remaining_vertical, last_slope). We use dynamic programming over the state space implied by the derivation, apply each admissible transition, and read the answer from the final table entry.

Pseudocode

Dynamic programming over run-length decompositions
State: (remaining_horizontal, remaining_vertical, last_slope)

Complexity Analysis

  • Time: O(N4)O(N^4) states times O(N2)O(N^2) transitions = O(N6)O(N^6) in the worst case; practical implementations with slope pruning are much faster.
  • Space: O(N4)O(N^4) for the memoization table.

Answer

1398582231101\boxed{1398582231101}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_604/solution.cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll catalan(int n) {
    ll c = 1;
    for (int i = 0; i < n; i++) {
        c = c * 2 * (2*i + 1) / (i + 2);
    }
    return c;
}

int main() {
    for (int n = 0; n <= 20; n++)
        cout << "C(" << n << ") = " << catalan(n) << endl;
    return 0;
}