All IOI entries
Competitive Programming

IOI 1989 - Problem 1: Packing Rectangles

Layout Enumeration There are exactly 6 topologically distinct ways to pack 4 axis-aligned rectangles into a bounding box. For rectangles labeled a, b, c, d with dimensions (w_i, h_i): Row: All four side by side. W = w...

Source sync Apr 19, 2026
Track IOI
Year 1989
Files TeX and C++
Folder competitive_programming/ioi/1989/packing_rectangles
IOI1989TeXC++

Source-first archive entry

This page is built from the copied files in competitive_programming/ioi/1989/packing_rectangles. Edit competitive_programming/ioi/1989/packing_rectangles/solution.tex to update the written solution and competitive_programming/ioi/1989/packing_rectangles/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

Rendered from the "Problem Statement" section inside solution.tex because this entry has no separate statement file.

Given 4 rectangles with specified widths and heights, find the enclosing rectangle of minimum area that contains all four without overlap. Each rectangle may be rotated by $90^\circ$. All rectangles must be placed with sides parallel to the sides of the enclosing rectangle.

Output the minimum area and all pairs $(W, H)$ with $W \le H$ that achieve this area.

Editorial

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

Solution

Layout Enumeration

There are exactly 6 topologically distinct ways to pack 4 axis-aligned rectangles into a bounding box. For rectangles labeled $a, b, c, d$ with dimensions $(w_i, h_i)$:

  1. Row: All four side by side.
    $W = w_a + w_b + w_c + w_d$, $H = \max(h_a, h_b, h_c, h_d)$.

  2. Three stacked + one beside: Three rectangles stacked vertically, one placed to their side.
    $W = \max(w_a, w_b, w_c) + w_d$, $H = \max(h_a + h_b + h_c,\; h_d)$.

  3. Two + two stacked: Two stacked on the left, two on the right.
    $W = \max(w_a, w_b) + \max(w_c, w_d)$, $H = \max(h_a + h_b,\; h_c + h_d)$.

  4. L-shape: Two stacked left ($a, b$); $c$ right of $a$; $d$ right of $b$.
    $W = \max(w_a + w_c,\; w_b + w_d)$, $H = \max(h_a + h_b,\; h_c + h_d)$.

  5. T-shape: $a$ on the left spanning full height; $b$ and $c$ stacked in the middle; $d$ on the right spanning full height.
    $W = w_a + \max(w_b, w_c) + w_d$, $H = \max(h_a,\; h_b + h_c,\; h_d)$.

  6. Cross: $a$ and $b$ on top row with a horizontal split; $c$ and $d$ on bottom row with a possibly different split. The height depends on how the vertical boundary interacts.
    This is handled by trying $c$ next to $a$ and $d$ next to $b$, etc.

Algorithm

For each of the $2^4 = 16$ rotation combinations and all $4! = 24$ permutations of the rectangles, evaluate all 6 layout types. Track the minimum area and, among ties, the minimum perimeter.

The total work is at most $16 \times 24 \times 6 = 2304$ evaluations, each taking $O(1)$ time.

Complexity Analysis

  • Time: $O(1)$ --- bounded by $2304$ constant-time evaluations.

  • Space: $O(1)$.

Implementation

#include <iostream>
#include <algorithm>
#include <set>
#include <climits>
using namespace std;

int bestArea;
set<pair<int,int>> bestDims;

void updateBest(int w, int h) {
    if (w <= 0 || h <= 0) return;
    int area = w * h;
    if (area < bestArea) {
        bestArea = area;
        bestDims.clear();
    }
    if (area == bestArea) {
        int lo = min(w, h), hi = max(w, h);
        bestDims.insert({lo, hi});
    }
}

void tryLayouts(int w[], int h[]) {
    // Type 1: all in a row
    updateBest(w[0]+w[1]+w[2]+w[3],
               max({h[0],h[1],h[2],h[3]}));

    // Type 2: three stacked, one beside
    for (int i = 0; i < 4; i++) {
        int sumH = 0, maxW = 0;
        for (int j = 0; j < 4; j++) {
            if (j == i) continue;
            sumH += h[j];
            maxW = max(maxW, w[j]);
        }
        updateBest(maxW + w[i], max(sumH, h[i]));
    }

    // Type 3: two stacked left, two stacked right (3 pairings)
    int pairs[3][4] = {{0,1,2,3},{0,2,1,3},{0,3,1,2}};
    for (auto& p : pairs) {
        int a=p[0], b=p[1], c=p[2], d=p[3];
        updateBest(max(w[a],w[b]) + max(w[c],w[d]),
                   max(h[a]+h[b], h[c]+h[d]));
    }

    // Types 4-5: L-shape and T-shape (all 24 permutations)
    int perm[4] = {0, 1, 2, 3};
    do {
        int a=perm[0], b=perm[1], c=perm[2], d=perm[3];

        // Type 4: a,b stacked left; c right of a; d right of b
        updateBest(max(w[a]+w[c], w[b]+w[d]),
                   max(h[a]+h[b], h[c]+h[d]));

        // Type 5: a left, (b,c) stacked middle, d right
        updateBest(w[a] + max(w[b],w[c]) + w[d],
                   max({h[a], h[b]+h[c], h[d]}));
    } while (next_permutation(perm, perm+4));
}

int main() {
    int W[4], H[4];
    for (int i = 0; i < 4; i++)
        cin >> W[i] >> H[i];

    bestArea = INT_MAX;

    for (int mask = 0; mask < 16; mask++) {
        int w[4], h[4];
        for (int i = 0; i < 4; i++) {
            if (mask & (1 << i)) {
                w[i] = H[i]; h[i] = W[i];
            } else {
                w[i] = W[i]; h[i] = H[i];
            }
        }
        tryLayouts(w, h);
    }

    cout << bestArea << endl;
    for (auto& [w, h] : bestDims)
        cout << w << " " << h << endl;

    return 0;
}

Notes

The solution exhaustively enumerates all topologically distinct packing configurations. Since the number of configurations is bounded by a small constant, correctness follows from completeness of the case analysis. The six layout types are well-known to cover all possible axis-aligned packings of four rectangles; see, e.g., Korf (2003) for a proof that these cases are exhaustive.

The improved code uses next_permutation for cleaner permutation enumeration and collects all optimal $(W, H)$ pairs in a set to avoid duplicates.

Code

Exact copied C++ implementation from solution.cpp.

C++ competitive_programming/ioi/1989/packing_rectangles/solution.cpp

Exact copied implementation source.

Raw file
// IOI 1989 - Problem 1: Packing Rectangles
// Find smallest enclosing rectangle for 4 rectangles (each may be rotated 90 degrees).
// Enumerate all 6 layout types x 24 permutations x 16 rotations.
#include <bits/stdc++.h>
using namespace std;

int bestArea, bestW, bestH;

void updateBest(int w, int h) {
    if (w <= 0 || h <= 0) return;
    int area = w * h;
    if (area < bestArea || (area == bestArea && w + h < bestW + bestH)) {
        bestArea = area;
        // Normalize so bestW <= bestH
        bestW = min(w, h);
        bestH = max(w, h);
    }
}

void tryLayouts(int w[], int h[]) {
    // Type 1: all four in a row
    updateBest(w[0] + w[1] + w[2] + w[3],
               max({h[0], h[1], h[2], h[3]}));

    // Type 2: three stacked on one side, one beside them
    for (int i = 0; i < 4; i++) {
        int sumH = 0, maxW = 0;
        for (int j = 0; j < 4; j++) {
            if (j == i) continue;
            sumH += h[j];
            maxW = max(maxW, w[j]);
        }
        updateBest(maxW + w[i], max(sumH, h[i]));
    }

    // Type 3: two stacked left, two stacked right (3 pairings)
    int pairs[3][4] = {{0,1,2,3}, {0,2,1,3}, {0,3,1,2}};
    for (auto& p : pairs) {
        int a = p[0], b = p[1], c = p[2], d = p[3];
        updateBest(max(w[a], w[b]) + max(w[c], w[d]),
                   max(h[a] + h[b], h[c] + h[d]));
    }

    // Types 4-5: enumerate all 24 permutations for 2x2-style layouts
    int idx[4];
    int perm[4] = {0, 1, 2, 3};
    do {
        int a = perm[0], b = perm[1], c = perm[2], d = perm[3];

        // Type 4: a,b stacked left; c right of a; d right of b
        updateBest(max(w[a] + w[c], w[b] + w[d]),
                   max(h[a] + h[b], h[c] + h[d]));

        // Type 5: a left column, (b,c) stacked middle, d right column
        updateBest(w[a] + max(w[b], w[c]) + w[d],
                   max({h[a], h[b] + h[c], h[d]}));
    } while (next_permutation(perm, perm + 4));
}

int main() {
    int W[4], H[4];
    for (int i = 0; i < 4; i++)
        scanf("%d%d", &W[i], &H[i]);

    bestArea = INT_MAX;
    bestW = bestH = INT_MAX;

    // Try all 2^4 = 16 rotation states
    for (int mask = 0; mask < 16; mask++) {
        int w[4], h[4];
        for (int i = 0; i < 4; i++) {
            if (mask & (1 << i)) {
                w[i] = H[i]; h[i] = W[i];
            } else {
                w[i] = W[i]; h[i] = H[i];
            }
        }
        tryLayouts(w, h);
    }

    printf("%d\n%d %d\n", bestArea, bestW, bestH);
    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/1989/packing_rectangles/solution.tex

Exact copied write-up source.

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

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

\title{IOI 1989 -- Problem 1: Packing Rectangles}
\author{Solution}
\date{}

\begin{document}
\maketitle

\section{Problem Statement}

Given 4 rectangles with specified widths and heights, find the enclosing
rectangle of minimum area that contains all four without overlap. Each
rectangle may be rotated by $90^\circ$. All rectangles must be placed with
sides parallel to the sides of the enclosing rectangle.

Output the minimum area and all pairs $(W, H)$ with $W \le H$ that achieve
this area.

\section{Solution}

\subsection{Layout Enumeration}

There are exactly \textbf{6 topologically distinct} ways to pack 4
axis-aligned rectangles into a bounding box. For rectangles labeled
$a, b, c, d$ with dimensions $(w_i, h_i)$:

\begin{enumerate}
  \item \textbf{Row:} All four side by side.\\
    $W = w_a + w_b + w_c + w_d$, \;
    $H = \max(h_a, h_b, h_c, h_d)$.

  \item \textbf{Three stacked + one beside:} Three rectangles stacked
    vertically, one placed to their side.\\
    $W = \max(w_a, w_b, w_c) + w_d$, \;
    $H = \max(h_a + h_b + h_c,\; h_d)$.

  \item \textbf{Two + two stacked:} Two stacked on the left, two on the right.\\
    $W = \max(w_a, w_b) + \max(w_c, w_d)$, \;
    $H = \max(h_a + h_b,\; h_c + h_d)$.

  \item \textbf{L-shape:} Two stacked left ($a, b$); $c$ right of $a$;
    $d$ right of $b$.\\
    $W = \max(w_a + w_c,\; w_b + w_d)$, \;
    $H = \max(h_a + h_b,\; h_c + h_d)$.

  \item \textbf{T-shape:} $a$ on the left spanning full height; $b$ and $c$
    stacked in the middle; $d$ on the right spanning full height.\\
    $W = w_a + \max(w_b, w_c) + w_d$, \;
    $H = \max(h_a,\; h_b + h_c,\; h_d)$.

  \item \textbf{Cross:} $a$ and $b$ on top row with a horizontal split; $c$
    and $d$ on bottom row with a possibly different split. The height depends
    on how the vertical boundary interacts.\\
    This is handled by trying $c$ next to $a$ and $d$ next to $b$, etc.
\end{enumerate}

\subsection{Algorithm}

For each of the $2^4 = 16$ rotation combinations and all $4! = 24$
permutations of the rectangles, evaluate all 6 layout types. Track the
minimum area and, among ties, the minimum perimeter.

The total work is at most $16 \times 24 \times 6 = 2304$ evaluations, each
taking $O(1)$ time.

\section{Complexity Analysis}

\begin{itemize}
  \item \textbf{Time:} $O(1)$ --- bounded by $2304$ constant-time evaluations.
  \item \textbf{Space:} $O(1)$.
\end{itemize}

\section{Implementation}

\begin{lstlisting}
#include <iostream>
#include <algorithm>
#include <set>
#include <climits>
using namespace std;

int bestArea;
set<pair<int,int>> bestDims;

void updateBest(int w, int h) {
    if (w <= 0 || h <= 0) return;
    int area = w * h;
    if (area < bestArea) {
        bestArea = area;
        bestDims.clear();
    }
    if (area == bestArea) {
        int lo = min(w, h), hi = max(w, h);
        bestDims.insert({lo, hi});
    }
}

void tryLayouts(int w[], int h[]) {
    // Type 1: all in a row
    updateBest(w[0]+w[1]+w[2]+w[3],
               max({h[0],h[1],h[2],h[3]}));

    // Type 2: three stacked, one beside
    for (int i = 0; i < 4; i++) {
        int sumH = 0, maxW = 0;
        for (int j = 0; j < 4; j++) {
            if (j == i) continue;
            sumH += h[j];
            maxW = max(maxW, w[j]);
        }
        updateBest(maxW + w[i], max(sumH, h[i]));
    }

    // Type 3: two stacked left, two stacked right (3 pairings)
    int pairs[3][4] = {{0,1,2,3},{0,2,1,3},{0,3,1,2}};
    for (auto& p : pairs) {
        int a=p[0], b=p[1], c=p[2], d=p[3];
        updateBest(max(w[a],w[b]) + max(w[c],w[d]),
                   max(h[a]+h[b], h[c]+h[d]));
    }

    // Types 4-5: L-shape and T-shape (all 24 permutations)
    int perm[4] = {0, 1, 2, 3};
    do {
        int a=perm[0], b=perm[1], c=perm[2], d=perm[3];

        // Type 4: a,b stacked left; c right of a; d right of b
        updateBest(max(w[a]+w[c], w[b]+w[d]),
                   max(h[a]+h[b], h[c]+h[d]));

        // Type 5: a left, (b,c) stacked middle, d right
        updateBest(w[a] + max(w[b],w[c]) + w[d],
                   max({h[a], h[b]+h[c], h[d]}));
    } while (next_permutation(perm, perm+4));
}

int main() {
    int W[4], H[4];
    for (int i = 0; i < 4; i++)
        cin >> W[i] >> H[i];

    bestArea = INT_MAX;

    for (int mask = 0; mask < 16; mask++) {
        int w[4], h[4];
        for (int i = 0; i < 4; i++) {
            if (mask & (1 << i)) {
                w[i] = H[i]; h[i] = W[i];
            } else {
                w[i] = W[i]; h[i] = H[i];
            }
        }
        tryLayouts(w, h);
    }

    cout << bestArea << endl;
    for (auto& [w, h] : bestDims)
        cout << w << " " << h << endl;

    return 0;
}
\end{lstlisting}

\section{Notes}

The solution exhaustively enumerates all topologically distinct packing
configurations. Since the number of configurations is bounded by a small
constant, correctness follows from completeness of the case analysis. The
six layout types are well-known to cover all possible axis-aligned packings
of four rectangles; see, e.g., Korf (2003) for a proof that these cases
are exhaustive.

The improved code uses \texttt{next\_permutation} for cleaner permutation
enumeration and collects all optimal $(W, H)$ pairs in a set to avoid
duplicates.

\end{document}