All IOI entries
Competitive Programming

IOI 1993 - Day 2, Task 2: The School Bus

Two-Phase Bitmask DP For small n (up to about 15), we use an exact algorithm in two phases. Phase 1: Optimal Tour for Each Subset (Held--Karp TSP) For each subset S of locations whose total student count does not exce...

Source sync Apr 19, 2026
Track IOI
Year 1993
Files TeX and C++
Folder competitive_programming/ioi/1993/school_bus
IOI1993TeXC++

Source-first archive entry

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

A school bus starts at a depot, picks up students at $n$ locations, and returns. The bus has capacity $C$, so it may need multiple round trips. Each location $i$ has coordinates $(x_i, y_i)$ and $s_i$ students. Minimize the total Euclidean distance traveled across all trips.

This is a variant of the Capacitated Vehicle Routing Problem (CVRP).

Editorial

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

Solution

Two-Phase Bitmask DP

For small $n$ (up to about 15), we use an exact algorithm in two phases.

Phase 1: Optimal Tour for Each Subset (Held--Karp TSP)

For each subset $S$ of locations whose total student count does not exceed $C$, compute the shortest round trip from the depot visiting all locations in $S$.

Define $\text{tsp}[S][v]$ as the minimum distance to visit every location in $S$, starting from the depot and ending at location $v$. The recurrence is: \[ \text{tsp}[S][v] = \min_{u \in S \setminus \{v\}} \bigl(\text{tsp}[S \setminus \{v\}][u] + d(u, v)\bigr), \] with base case $\text{tsp}[\{v\}][v] = d(\text{depot}, v)$.

The round-trip cost for subset $S$ is: \[ \text{tour}[S] = \min_{v \in S} \bigl(\text{tsp}[S][v] + d(v, \text{depot})\bigr). \]

Phase 2: Set Partition DP

Partition all $n$ locations into capacity-feasible trips to minimize total cost: \[ \text{dp}[S] = \min_{\substack{T \subseteq S,\; T \ne \emptyset \\ \text{students}(T) \le C}} \bigl(\text{tour}[T] + \text{dp}[S \setminus T]\bigr), \] with $\text{dp}[\emptyset] = 0$.

Subsets of $S$ are enumerated using the standard bitmask trick: for (T = S; T > 0; T = (T-1) & S).

Complexity Analysis

  • Phase 1 (TSP): $O(2^n \cdot n^2)$ time, $O(2^n \cdot n)$ space.

  • Phase 2 (partition): $O(3^n)$ time (sum over all $S$ of the number of subsets of $S$ equals $3^n$).

  • Total: $O(2^n \cdot n^2 + 3^n)$ time, $O(2^n \cdot n)$ space.

  • Feasible for $n \le 15$.

Implementation

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

const int MAXN = 16;
const double INF = 1e18;

int n, capacity;
int students[MAXN];
double dist_mat[MAXN+1][MAXN+1]; // 0 = depot, 1..n = locations

double tspDp[1 << MAXN][MAXN];
double tourCost[1 << MAXN];
int totalStudents[1 << MAXN];
bool feasible[1 << MAXN];
double partDp[1 << MAXN];

int main() {
    cin >> n >> capacity;

    double dx[MAXN+1], dy[MAXN+1];
    cin >> dx[0] >> dy[0]; // depot
    for (int i = 1; i <= n; i++)
        cin >> dx[i] >> dy[i] >> students[i-1];

    // Distance matrix
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            dist_mat[i][j] = hypot(dx[i]-dx[j], dy[i]-dy[j]);

    int full = (1 << n) - 1;

    // Precompute total students per subset
    for (int S = 0; S <= full; S++) {
        totalStudents[S] = 0;
        for (int i = 0; i < n; i++)
            if (S & (1 << i))
                totalStudents[S] += students[i];
        feasible[S] = (totalStudents[S] <= capacity);
    }

    // Phase 1: TSP DP
    for (int S = 0; S <= full; S++)
        for (int i = 0; i < n; i++)
            tspDp[S][i] = INF;

    for (int i = 0; i < n; i++)
        tspDp[1 << i][i] = dist_mat[0][i+1];

    for (int S = 1; S <= full; S++) {
        if (!feasible[S]) continue;
        for (int v = 0; v < n; v++) {
            if (!(S & (1 << v))) continue;
            if (tspDp[S][v] >= INF) continue;
            for (int u = 0; u < n; u++) {
                if (S & (1 << u)) continue;
                int newS = S | (1 << u);
                if (!feasible[newS]) continue;
                double cost = tspDp[S][v] + dist_mat[v+1][u+1];
                if (cost < tspDp[newS][u])
                    tspDp[newS][u] = cost;
            }
        }
    }

    // Tour cost for each feasible subset
    for (int S = 0; S <= full; S++) {
        tourCost[S] = INF;
        if (!feasible[S] || S == 0) continue;
        for (int v = 0; v < n; v++) {
            if (!(S & (1 << v))) continue;
            double cost = tspDp[S][v] + dist_mat[v+1][0];
            tourCost[S] = min(tourCost[S], cost);
        }
    }

    // Phase 2: Set partition DP
    for (int S = 0; S <= full; S++)
        partDp[S] = INF;
    partDp[0] = 0;

    for (int S = 1; S <= full; S++) {
        for (int T = S; T > 0; T = (T - 1) & S) {
            if (feasible[T] && tourCost[T] < INF) {
                double val = partDp[S ^ T] + tourCost[T];
                if (val < partDp[S])
                    partDp[S] = val;
            }
        }
    }

    cout.precision(2);
    cout << fixed << partDp[full] << endl;

    return 0;
}

Example

Input:
4 6
0 0              (depot)
1 1 3            (location 1: 3 students)
3 1 2            (location 2: 2 students)
2 3 4            (location 3: 4 students)
4 2 1            (location 4: 1 student)

Total students = 10, capacity = 6.
At least 2 trips are needed.

The DP finds the optimal partition into trips and the shortest tour for each.

Notes

  • This problem is NP-hard in general (it contains TSP as a special case when $C \ge \sum s_i$).

  • The bitmask DP gives an exact solution for $n \le 15$.

  • For larger $n$, heuristics (nearest-neighbor, savings algorithm) or metaheuristics (simulated annealing) are necessary.

  • The subset enumeration identity $\sum_{S \subseteq [n]} 2^{|S|} = 3^n$ explains the $O(3^n)$ time for Phase 2.

Code

Exact copied C++ implementation from solution.cpp.

C++ competitive_programming/ioi/1993/school_bus/solution.cpp

Exact copied implementation source.

Raw file
// IOI 1993 - Day 2, Task 2: The School Bus
// Capacitated VRP: bus makes multiple round trips from depot.
// Bitmask DP: TSP on feasible subsets + set partition DP.
// O(2^n * n^2 + 3^n), feasible for n <= 15.
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 16;
const double INF = 1e18;

int n, capacity;
int students[MAXN];
double dist_mat[MAXN + 1][MAXN + 1]; // 0 = depot, 1..n = locations
double tspDp[1 << MAXN][MAXN];
double tourCost[1 << MAXN];
int totalStudents[1 << MAXN];
bool feasible[1 << MAXN];
double partDp[1 << MAXN];

int main() {
    scanf("%d%d", &n, &capacity);

    double dx[MAXN + 1], dy[MAXN + 1];
    scanf("%lf%lf", &dx[0], &dy[0]); // depot
    for (int i = 1; i <= n; i++)
        scanf("%lf%lf%d", &dx[i], &dy[i], &students[i - 1]);

    // Compute distance matrix
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            dist_mat[i][j] = hypot(dx[i] - dx[j], dy[i] - dy[j]);

    int full = (1 << n) - 1;

    // Precompute feasibility for each subset
    for (int S = 0; S <= full; S++) {
        totalStudents[S] = 0;
        for (int i = 0; i < n; i++)
            if (S & (1 << i))
                totalStudents[S] += students[i];
        feasible[S] = (totalStudents[S] <= capacity);
    }

    // TSP DP: tspDp[S][v] = min cost from depot visiting all in S, ending at v
    for (int S = 0; S <= full; S++)
        for (int i = 0; i < n; i++)
            tspDp[S][i] = INF;

    for (int i = 0; i < n; i++)
        tspDp[1 << i][i] = dist_mat[0][i + 1];

    for (int S = 1; S <= full; S++) {
        if (!feasible[S]) continue;
        for (int v = 0; v < n; v++) {
            if (!(S & (1 << v)) || tspDp[S][v] >= INF) continue;
            for (int u = 0; u < n; u++) {
                if (S & (1 << u)) continue;
                int newS = S | (1 << u);
                if (!feasible[newS]) continue;
                double cost = tspDp[S][v] + dist_mat[v + 1][u + 1];
                if (cost < tspDp[newS][u])
                    tspDp[newS][u] = cost;
            }
        }
    }

    // Compute round-trip tour cost for each feasible subset
    for (int S = 0; S <= full; S++) {
        tourCost[S] = INF;
        if (!feasible[S]) continue;
        if (S == 0) { tourCost[S] = 0; continue; }
        for (int v = 0; v < n; v++) {
            if (!(S & (1 << v))) continue;
            tourCost[S] = min(tourCost[S], tspDp[S][v] + dist_mat[v + 1][0]);
        }
    }

    // Set partition DP: partition all locations into feasible trips
    for (int S = 0; S <= full; S++) partDp[S] = INF;
    partDp[0] = 0;

    for (int S = 1; S <= full; S++) {
        for (int T = S; T > 0; T = (T - 1) & S) {
            if (feasible[T] && tourCost[T] < INF)
                partDp[S] = min(partDp[S], partDp[S ^ T] + tourCost[T]);
        }
    }

    printf("%.2f\n", partDp[full]);
    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/1993/school_bus/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 1993 -- Day 2, Task 2: The School Bus}
\author{Solution}
\date{}

\begin{document}
\maketitle

\section{Problem Statement}

A school bus starts at a depot, picks up students at $n$ locations, and
returns. The bus has capacity $C$, so it may need multiple round trips.
Each location $i$ has coordinates $(x_i, y_i)$ and $s_i$ students. Minimize
the total Euclidean distance traveled across all trips.

This is a variant of the \textbf{Capacitated Vehicle Routing Problem (CVRP)}.

\section{Solution}

\subsection{Two-Phase Bitmask DP}

For small $n$ (up to about 15), we use an exact algorithm in two phases.

\subsubsection{Phase 1: Optimal Tour for Each Subset (Held--Karp TSP)}

For each subset $S$ of locations whose total student count does not exceed
$C$, compute the shortest round trip from the depot visiting all locations
in $S$.

Define $\text{tsp}[S][v]$ as the minimum distance to visit every location in
$S$, starting from the depot and ending at location $v$. The recurrence is:
\[
  \text{tsp}[S][v] = \min_{u \in S \setminus \{v\}}
    \bigl(\text{tsp}[S \setminus \{v\}][u] + d(u, v)\bigr),
\]
with base case $\text{tsp}[\{v\}][v] = d(\text{depot}, v)$.

The round-trip cost for subset $S$ is:
\[
  \text{tour}[S] = \min_{v \in S}
    \bigl(\text{tsp}[S][v] + d(v, \text{depot})\bigr).
\]

\subsubsection{Phase 2: Set Partition DP}

Partition all $n$ locations into capacity-feasible trips to minimize total
cost:
\[
  \text{dp}[S] = \min_{\substack{T \subseteq S,\; T \ne \emptyset \\
    \text{students}(T) \le C}}
    \bigl(\text{tour}[T] + \text{dp}[S \setminus T]\bigr),
\]
with $\text{dp}[\emptyset] = 0$.

Subsets of $S$ are enumerated using the standard bitmask trick:
\texttt{for (T = S; T > 0; T = (T-1) \& S)}.

\section{Complexity Analysis}

\begin{itemize}
  \item \textbf{Phase 1 (TSP):} $O(2^n \cdot n^2)$ time, $O(2^n \cdot n)$
    space.
  \item \textbf{Phase 2 (partition):} $O(3^n)$ time (sum over all $S$ of the
    number of subsets of $S$ equals $3^n$).
  \item \textbf{Total:} $O(2^n \cdot n^2 + 3^n)$ time, $O(2^n \cdot n)$
    space.
  \item Feasible for $n \le 15$.
\end{itemize}

\section{Implementation}

\begin{lstlisting}
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

const int MAXN = 16;
const double INF = 1e18;

int n, capacity;
int students[MAXN];
double dist_mat[MAXN+1][MAXN+1]; // 0 = depot, 1..n = locations

double tspDp[1 << MAXN][MAXN];
double tourCost[1 << MAXN];
int totalStudents[1 << MAXN];
bool feasible[1 << MAXN];
double partDp[1 << MAXN];

int main() {
    cin >> n >> capacity;

    double dx[MAXN+1], dy[MAXN+1];
    cin >> dx[0] >> dy[0]; // depot
    for (int i = 1; i <= n; i++)
        cin >> dx[i] >> dy[i] >> students[i-1];

    // Distance matrix
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++)
            dist_mat[i][j] = hypot(dx[i]-dx[j], dy[i]-dy[j]);

    int full = (1 << n) - 1;

    // Precompute total students per subset
    for (int S = 0; S <= full; S++) {
        totalStudents[S] = 0;
        for (int i = 0; i < n; i++)
            if (S & (1 << i))
                totalStudents[S] += students[i];
        feasible[S] = (totalStudents[S] <= capacity);
    }

    // Phase 1: TSP DP
    for (int S = 0; S <= full; S++)
        for (int i = 0; i < n; i++)
            tspDp[S][i] = INF;

    for (int i = 0; i < n; i++)
        tspDp[1 << i][i] = dist_mat[0][i+1];

    for (int S = 1; S <= full; S++) {
        if (!feasible[S]) continue;
        for (int v = 0; v < n; v++) {
            if (!(S & (1 << v))) continue;
            if (tspDp[S][v] >= INF) continue;
            for (int u = 0; u < n; u++) {
                if (S & (1 << u)) continue;
                int newS = S | (1 << u);
                if (!feasible[newS]) continue;
                double cost = tspDp[S][v] + dist_mat[v+1][u+1];
                if (cost < tspDp[newS][u])
                    tspDp[newS][u] = cost;
            }
        }
    }

    // Tour cost for each feasible subset
    for (int S = 0; S <= full; S++) {
        tourCost[S] = INF;
        if (!feasible[S] || S == 0) continue;
        for (int v = 0; v < n; v++) {
            if (!(S & (1 << v))) continue;
            double cost = tspDp[S][v] + dist_mat[v+1][0];
            tourCost[S] = min(tourCost[S], cost);
        }
    }

    // Phase 2: Set partition DP
    for (int S = 0; S <= full; S++)
        partDp[S] = INF;
    partDp[0] = 0;

    for (int S = 1; S <= full; S++) {
        for (int T = S; T > 0; T = (T - 1) & S) {
            if (feasible[T] && tourCost[T] < INF) {
                double val = partDp[S ^ T] + tourCost[T];
                if (val < partDp[S])
                    partDp[S] = val;
            }
        }
    }

    cout.precision(2);
    cout << fixed << partDp[full] << endl;

    return 0;
}
\end{lstlisting}

\section{Example}

\begin{verbatim}
Input:
4 6
0 0              (depot)
1 1 3            (location 1: 3 students)
3 1 2            (location 2: 2 students)
2 3 4            (location 3: 4 students)
4 2 1            (location 4: 1 student)

Total students = 10, capacity = 6.
At least 2 trips are needed.
\end{verbatim}

The DP finds the optimal partition into trips and the shortest tour for each.

\section{Notes}

\begin{itemize}
  \item This problem is NP-hard in general (it contains TSP as a special case
    when $C \ge \sum s_i$).
  \item The bitmask DP gives an exact solution for $n \le 15$.
  \item For larger $n$, heuristics (nearest-neighbor, savings algorithm) or
    metaheuristics (simulated annealing) are necessary.
  \item The subset enumeration identity $\sum_{S \subseteq [n]} 2^{|S|} = 3^n$
    explains the $O(3^n)$ time for Phase~2.
\end{itemize}

\end{document}