UVA - 1368 - Nested Dolls 

This article will explain my thought process to solve a problem called Nested Dolls (PDF) from UVA Online Judge. Read the original problem statement before reading this article.

Keywords: Dynamic Programming

Objective 

Given:

  1. Number of dolls.

  2. Dimension (Width and Height) of the dolls.

  3. Rules to create a nested dolls:

    1. Both width and height of the inner doll is lesser than the outer doll.

We want to:

  1. Find the minimum number of nested dolls.

Glossary 

  1. Dynamic Programming (DP) — A method used to solve complex problems by breaking them into smaller overlapping subproblems and storing their results to avoid recomputation.

  2. Directed Acyclic Graph (DAG) — A directed graph with no directed cycles.

Observation 

Imagine that we put the dimension (width and height) of the dolls in a 2 dimensional grid. Based on the position of the outer doll, we could split the grid into 4 sections of A (upper-left), B (upper-right), C (lower-left) and D (lower-right). Section D is for the outer doll, section A is for the dolls that can be nested, and the remaining sections are for the doll that can’t be nested.

We want to nest the dolls as much as possible to minimize the number of nested dolls. This problem can be seen as a DAG as well, because we are moving in one direction from section A to section D to nest the dolls. DAG is an excellent indicator to determine whether a problem can be solved with DP strategy.

To solve this problem with DP, we first need to breakdown the main problem. We want to nest the dolls as much as possible, and thus we want to find the most optimal doll from section A that can be nested into doll in section D.

Section A and D is separated by the width, thus by iteratively going from the least to the greatest width, we should be able to discover dolls on section A+B (dolls with lesser width), C (dolls with identical width) and D (current doll). Next, we could take advantage of the height to separate A and B. If the dolls on section A+B is sorted by the height, we could find dolls on section A in logarithmic time using the D’s height. After that, how do we determine which dolls is the best fit for D? The width is relevant at this point due to the nature of the iterative process (least-to-greatest width), only the height matters. Less height means it can accommodate a much smaller height for the outer doll. This means less height (on section A) has more coverage (on section D) and we want to avoid taking the least height, unless necessary. We could say that the best fit is the doll from section A with maximum amount of height that can be accommodated by the current doll.

During each iteration of finding the best fit above, we keep track of the outer dolls while removing an outer doll that become an inner dolls on each iteration. At the end of the process, we will have the minimum number of nested dolls.

Algorithm 

Since we want to process the dolls from the least-to-greatest width, we first need to sort the dimension of the dolls using its width. On each iteration, we keep track of the dolls with lesser width (section A+B), dolls with equals width (section C), and current doll (section D). To find the dolls with maximum height and lesser width, we keep track of the dolls with lesser width using a tree data structure that is sorted by the height, and then query it using the current doll height. Since we are only interested in the outer dolls, this maximum height dolls can be removed from the list of dolls with lesser width.

The time complexity of this solution is O({total dolls} * log2({total dolls})) or O(10^5).

Implementation 

import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedList; import java.util.TreeSet; /** * 11368 - Nested Dolls * Time limit: 1.000 seconds * https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=2353 */ public class Main { public static void main(final String... args) throws IOException { final BufferedIO io = new BufferedIO(System.in, System.out); final Process process = new Process(); final int totalCases = Integer.parseInt(io.readLine()); for (int i = 0; i < totalCases; i++) { final Input input = new Input(); input.totalDolls = Integer.parseInt(io.readLine()); input.dimensions = new int[input.totalDolls][2]; final String[] dimensionLines = io.readLine(" "); for (int j = 0, k = 0; j < input.totalDolls; j++) { input.dimensions[j][0] = Integer.parseInt(dimensionLines[k++]); input.dimensions[j][1] = Integer.parseInt(dimensionLines[k++]); } final Output output = process.process(input); io.write("%d\n", output.totalNestedDolls); } io.close(); } } class Input { public int totalDolls; public int[][] dimensions; } class Output { public int totalNestedDolls; } class Process { public Output process(final Input input) { final Doll[] dolls = new Doll[input.totalDolls]; for (int i = 0; i < input.totalDolls; i++) { final int[] dimension = input.dimensions[i]; final Doll doll = new Doll(i, dimension[0], dimension[1]); dolls[i] = doll; } Arrays.sort(dolls, Doll.ORDER_BY_WIDTH); final TreeSet<Doll> previousWidthDolls = new TreeSet<>(Doll.ORDER_BY_HEIGHT); final LinkedList<Doll> currentWidthDolls = new LinkedList<>(); for (final Doll doll : dolls) { final boolean isCurrentWidth = currentWidthDolls.isEmpty() || doll.width == currentWidthDolls.getFirst().width; if (!isCurrentWidth) { previousWidthDolls.addAll(currentWidthDolls); currentWidthDolls.clear(); } currentWidthDolls.addLast(doll); final Doll query = new Doll(-1, doll.width, doll.height); final Doll innerDoll = previousWidthDolls.lower(query); if (innerDoll != null) { previousWidthDolls.remove(innerDoll); } } previousWidthDolls.addAll(currentWidthDolls); currentWidthDolls.clear(); final Output output = new Output(); output.totalNestedDolls = previousWidthDolls.size(); return output; } } class Doll { public static final Comparator<Doll> ORDER_BY_WIDTH = Comparator .comparingInt((Doll d) -> d.width) .thenComparingInt((Doll d) -> d.id); public static final Comparator<Doll> ORDER_BY_HEIGHT = Comparator .comparingInt((Doll d) -> d.height) .thenComparingInt((Doll d) -> d.id); public final int id; public final int width; public final int height; public Doll(final int id, final int width, final int height) { this.id = id; this.width = width; this.height = height; } } final class BufferedIO { private final BufferedReader in; private final BufferedWriter out; public BufferedIO(final InputStream in, final OutputStream out) { this.in = new BufferedReader(new InputStreamReader(in)); this.out = new BufferedWriter(new OutputStreamWriter(out)); } public String[] readLine(final String separator) throws IOException { final String line = readLine(); return line == null ? null : line.split(separator); } public String readLine() throws IOException { String line = in.readLine(); while (line != null && line.isEmpty()) line = in.readLine(); return line; } public void write(final String format, Object... args) throws IOException { final String string = String.format(format, args); write(string); } public void write(final String string) throws IOException { out.write(string); } public void close() throws IOException { in.close(); out.flush(); out.close(); } }


Powered by Docmost