All IOI entries
Competitive Programming

IOI 2004 - Phidias

Problem Statement Summary Given a rectangular marble slab of size W H (W, H 600) and N desired piece sizes (w_i, h_i), cut the slab by making full-width or full-height cuts (each cut traverses the entire current piece...

Source sync Apr 19, 2026
Track IOI
Year 2004
Files TeX and C++
Folder competitive_programming/ioi/2004/phidias
IOI2004TeXC++

Source-first archive entry

This page is built from the copied files in competitive_programming/ioi/2004/phidias. Edit competitive_programming/ioi/2004/phidias/solution.tex to update the written solution and competitive_programming/ioi/2004/phidias/solution.cpp to update the implementation.

The website does not replace those files with hand-maintained HTML. It reads the copied source tree during the build and exposes the exact files below.

Problem Statement

This archive entry does not include a standalone statement file or a dedicated statement section in solution.tex.

The statement is not mirrored as a separate asset in this archive. The editorial and source files remain available below.

Editorial

Rendered from the copied solution.tex file. The original TeX source remains available below.

Problem Statement Summary

Given a rectangular marble slab of size $W \times H$ ($W, H \le 600$) and $N$ desired piece sizes $(w_i, h_i)$, cut the slab by making full-width or full-height cuts (each cut traverses the entire current piece). Minimize the total wasted area (area not covered by any desired piece).

Solution: 2D DP on Rectangle Dimensions

Recurrence

Every cut splits a rectangle into two sub-rectangles. Define $\mathrm{dp}[a][b]$ = minimum waste when optimally cutting a rectangle of size $a \times b$.

\[ \mathrm{dp}[a][b] = \min\Bigl( \min_{\substack{w \in \mathcal{W} \\ 0 < w < a}} \bigl(\mathrm{dp}[w][b] + \mathrm{dp}[a{-}w][b]\bigr),\; \min_{\substack{h \in \mathcal{H} \\ 0 < h < b}} \bigl(\mathrm{dp}[a][h] + \mathrm{dp}[a][b{-}h]\bigr) \Bigr) \] where $\mathcal{W}$ and $\mathcal{H}$ are the sets of widths and heights appearing among the desired pieces.

Base cases: $\mathrm{dp}[a][b] = a \cdot b$ (all waste) unless $(a, b)$ matches a desired piece, in which case $\mathrm{dp}[a][b] = 0$.

Lemma (Cut position restriction).

It suffices to cut only at positions in $\mathcal{W}$ (for vertical cuts) or $\mathcal{H}$ (for horizontal cuts). Cutting at any other position cannot produce a desired piece boundary, so it cannot reduce waste below what these cuts achieve.

C++ Implementation

#include <bits/stdc++.h>
using namespace std;

int dp[601][601];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int W, H, N;
    cin >> W >> H >> N;

    vector<int> pw(N), ph(N);
    set<int> ws, hs;
    set<pair<int,int>> pieces;

    for (int i = 0; i < N; i++) {
        cin >> pw[i] >> ph[i];
        ws.insert(pw[i]);
        hs.insert(ph[i]);
        pieces.insert({pw[i], ph[i]});
    }

    for (int a = 1; a <= W; a++) {
        for (int b = 1; b <= H; b++) {
            dp[a][b] = a * b; // worst case: all waste

            if (pieces.count({a, b})) {
                dp[a][b] = 0;
                continue;
            }

            // Vertical cuts at widths from desired pieces
            for (int w : ws) {
                if (w >= a) break;
                dp[a][b] = min(dp[a][b], dp[w][b] + dp[a - w][b]);
            }

            // Horizontal cuts at heights from desired pieces
            for (int h : hs) {
                if (h >= b) break;
                dp[a][b] = min(dp[a][b], dp[a][h] + dp[a][b - h]);
            }
        }
    }

    cout << dp[W][H] << "\n";
    return 0;
}

Complexity Analysis

  • Time: $O(W \cdot H \cdot N)$. For each of the $W \times H$ states, we try at most $|\mathcal{W}| + |\mathcal{H}| \le 2N$ cut positions.

  • Space: $O(W \cdot H)$.

  • With $W, H \le 600$ and $N \le 600$, the total work is at most $600 \times 600 \times 1200 \approx 4.3 \times 10^8$, which is tight but feasible. The break when the cut position exceeds the current dimension prunes many iterations in practice.

Code

Exact copied C++ implementation from solution.cpp.

C++ competitive_programming/ioi/2004/phidias/solution.cpp

Exact copied implementation source.

Raw file
// IOI 2004 - Phidias
// Minimize waste when cutting a W x H slab into desired piece sizes.
// DP on dimensions; cuts only at desired piece widths/heights.
// O(W * H * N) where N = number of piece types.
#include <bits/stdc++.h>
using namespace std;

int dp[601][601];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int W, H;
    cin >> W >> H;

    int N;
    cin >> N;

    vector<int> pw(N), ph(N);
    set<int> ws, hs;
    set<pair<int, int>> pieces;
    for (int i = 0; i < N; i++) {
        cin >> pw[i] >> ph[i];
        ws.insert(pw[i]);
        hs.insert(ph[i]);
        pieces.insert({pw[i], ph[i]});
    }

    for (int a = 1; a <= W; a++) {
        for (int b = 1; b <= H; b++) {
            if (pieces.count({a, b})) {
                dp[a][b] = 0;
                continue;
            }
            dp[a][b] = a * b; // worst case: all waste

            // Vertical cuts at desired piece widths
            for (int w : ws) {
                if (w >= a) break;
                dp[a][b] = min(dp[a][b], dp[w][b] + dp[a - w][b]);
            }
            // Horizontal cuts at desired piece heights
            for (int h : hs) {
                if (h >= b) break;
                dp[a][b] = min(dp[a][b], dp[a][h] + dp[a][b - h]);
            }
        }
    }

    cout << dp[W][H] << "\n";
    return 0;
}

Source Files

Exact copied source-of-truth files. Edit solution.tex for the write-up and solution.cpp for the implementation.

TeX write-up competitive_programming/ioi/2004/phidias/solution.tex

Exact copied write-up source.

Raw file
\documentclass[11pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage{amsmath,amssymb,amsthm}
\usepackage{listings}
\usepackage{xcolor}
\usepackage[margin=1in]{geometry}
\usepackage{hyperref}

\newtheorem{theorem}{Theorem}
\newtheorem{lemma}[theorem]{Lemma}

\lstset{
  language=C++,
  basicstyle=\ttfamily\small,
  keywordstyle=\color{blue}\bfseries,
  commentstyle=\color{green!60!black},
  stringstyle=\color{red},
  numbers=left,
  numberstyle=\tiny\color{gray},
  breaklines=true,
  frame=single,
  tabsize=4,
  showstringspaces=false
}

\title{IOI 2004 -- Phidias}
\author{Solution}
\date{}

\begin{document}
\maketitle

\section{Problem Statement Summary}
Given a rectangular marble slab of size $W \times H$ ($W, H \le 600$)
and $N$ desired piece sizes $(w_i, h_i)$, cut the slab by making
full-width or full-height cuts (each cut traverses the entire current
piece). Minimize the total wasted area (area not covered by any desired
piece).

\section{Solution: 2D DP on Rectangle Dimensions}

\subsection{Recurrence}
Every cut splits a rectangle into two sub-rectangles. Define
$\mathrm{dp}[a][b]$ = minimum waste when optimally cutting a rectangle
of size $a \times b$.

\[
  \mathrm{dp}[a][b] = \min\Bigl(
    \min_{\substack{w \in \mathcal{W} \\ 0 < w < a}}
      \bigl(\mathrm{dp}[w][b] + \mathrm{dp}[a{-}w][b]\bigr),\;
    \min_{\substack{h \in \mathcal{H} \\ 0 < h < b}}
      \bigl(\mathrm{dp}[a][h] + \mathrm{dp}[a][b{-}h]\bigr)
  \Bigr)
\]
where $\mathcal{W}$ and $\mathcal{H}$ are the sets of widths and
heights appearing among the desired pieces.

Base cases: $\mathrm{dp}[a][b] = a \cdot b$ (all waste) unless
$(a, b)$ matches a desired piece, in which case $\mathrm{dp}[a][b] = 0$.

\begin{lemma}[Cut position restriction]
It suffices to cut only at positions in $\mathcal{W}$ (for vertical
cuts) or $\mathcal{H}$ (for horizontal cuts). Cutting at any other
position cannot produce a desired piece boundary, so it cannot reduce
waste below what these cuts achieve.
\end{lemma}

\section{C++ Implementation}

\begin{lstlisting}
#include <bits/stdc++.h>
using namespace std;

int dp[601][601];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int W, H, N;
    cin >> W >> H >> N;

    vector<int> pw(N), ph(N);
    set<int> ws, hs;
    set<pair<int,int>> pieces;

    for (int i = 0; i < N; i++) {
        cin >> pw[i] >> ph[i];
        ws.insert(pw[i]);
        hs.insert(ph[i]);
        pieces.insert({pw[i], ph[i]});
    }

    for (int a = 1; a <= W; a++) {
        for (int b = 1; b <= H; b++) {
            dp[a][b] = a * b; // worst case: all waste

            if (pieces.count({a, b})) {
                dp[a][b] = 0;
                continue;
            }

            // Vertical cuts at widths from desired pieces
            for (int w : ws) {
                if (w >= a) break;
                dp[a][b] = min(dp[a][b], dp[w][b] + dp[a - w][b]);
            }

            // Horizontal cuts at heights from desired pieces
            for (int h : hs) {
                if (h >= b) break;
                dp[a][b] = min(dp[a][b], dp[a][h] + dp[a][b - h]);
            }
        }
    }

    cout << dp[W][H] << "\n";
    return 0;
}
\end{lstlisting}

\section{Complexity Analysis}
\begin{itemize}
  \item \textbf{Time:} $O(W \cdot H \cdot N)$. For each of the
        $W \times H$ states, we try at most $|\mathcal{W}| + |\mathcal{H}|
        \le 2N$ cut positions.
  \item \textbf{Space:} $O(W \cdot H)$.
\end{itemize}

With $W, H \le 600$ and $N \le 600$, the total work is at most
$600 \times 600 \times 1200 \approx 4.3 \times 10^8$, which is tight
but feasible. The \texttt{break} when the cut position exceeds the
current dimension prunes many iterations in practice.

\end{document}