Skip to content
dsa450for five year olds

Heap · 14 of 18

Connect n ropes with minimum cost

Tie many ropes into one, paying the length of every new rope you make, and always tie the two shortest first.

Simple way
time O(n^2)
room O(n)
Clever way
time O(n log n)
room O(n)

The problem, simply

You get n ropes with different lengths. You can tie two ropes into one. Tying costs as much as the length of the new rope, so tying a 2 and a 3 costs 5. Keep tying until one rope is left. Find the smallest total cost.

For ropes [4, 3, 2, 6] the answer is 29.

A story to remember it

Picture it

heapbasket of ropes (min heap)
243601232436

All four ropes in a min heap. The shortest rope, 2, sits on top, so it is the first one we grab.

  • looking now

Examples

A normal case

In
ropes = [4, 3, 2, 6]
Out
29

Tie 2 and 3 for 5. Tie 4 and 5 for 9. Tie 6 and 9 for 15. Total 5 + 9 + 15 = 29.

The new rope is not always used next

In
ropes = [5, 5, 6, 6]
Out
44

Tie 5 and 5 for 10. Now the two shortest are 6 and 6, not 10, so tie them for 12. Then 10 and 12 for 22. Total 10 + 12 + 22 = 44. Always chaining onto the new rope would cost 48.

All the same

In
ropes = [1, 1, 1, 1]
Out
8

1 + 1 = 2, then 1 + 1 = 2, then 2 + 2 = 4. Total 8.

One rope or none

In
ropes = [7]
Out
0

There is nothing to tie, so it costs nothing. An empty list also gives 0.

Way 1: the simple way

Do the greedy idea with a plain list. Each round, walk the whole list to find the two shortest ropes, tie them, and put the new rope back.

  1. While more than one rope is left:
  2. Scan the list for the shortest and second shortest rope.
  3. Remove both, add their sum to the cost, and add the new rope to the list.
java
import java.util.ArrayList;
import java.util.List;

class Solution {
    public long minCost(int[] ropes) {
        List<Long> list = new ArrayList<>();
        for (int r : ropes) list.add((long) r);
        long total = 0;
        while (list.size() > 1) {
            long a = removeShortest(list);   // walks the whole list
            long b = removeShortest(list);
            total += a + b;
            list.add(a + b);
        }
        return total;
    }

    private long removeShortest(List<Long> list) {
        int best = 0;
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i) < list.get(best)) best = i;
        }
        long v = list.get(best);
        list.set(best, list.get(list.size() - 1));  // fill the hole with the last one
        list.remove(list.size() - 1);
        return v;
    }
}

How long it takes: there are n - 1 rounds and each scans up to n ropes, so O(n^2). Extra room: the list, O(n).

Way 2: the clever way

Keep the ropes in a min heap, so the shortest rope is always on top and grabbing it takes only log n steps.

  1. Put every rope length into a min heap.
  2. While the heap holds more than one rope:
    • Poll the shortest, a. Poll the next shortest, b.
    • Add a + b to the total cost.
    • Offer the new rope a + b back into the heap.
  3. Return the total.

Tie [4, 3, 2, 6] with a min heap

01 / 11

heapbasket of ropes (min heap)
243601232436

All four ropes are in the min heap. The shortest, 2, is on top.

total
0
  • looking now
  • checking
  • answer
  • just added
  • ignored
java
import java.util.PriorityQueue;

class Solution {
    public long minCost(int[] ropes) {
        PriorityQueue<Long> basket = new PriorityQueue<>();  // min heap
        for (int r : ropes) basket.offer((long) r);
        long total = 0;
        while (basket.size() > 1) {
            long a = basket.poll();          // shortest
            long b = basket.poll();          // next shortest
            total += a + b;                  // pay for the new rope
            basket.offer(a + b);             // the new rope goes back in
        }
        return total;
    }
}

How long it takes: n - 1 rounds, each with two polls and one offer at O(log n), so O(n log n). Extra room: the heap, O(n).

Why shortest first is right

Draw the knots as a tree. Each rope ends up as a leaf, and a rope is paid for once for every knot above it. So the total cost is each rope length times its depth in the tree.

tree
15694523paid 1 timepaid 2 timespaid 3 timespaid 3 times

The knot tree for [4, 3, 2, 6]. 2 and 3 are three knots deep, 4 is two deep, 6 is one deep. Cost = 2x3 + 3x3 + 4x2 + 6x1 = 29.

  • done
  • answer

The two shortest ropes should be the deepest leaves. If a longer rope sat deeper than a shorter one, swapping them would lower the cost. Tying the two shortest first puts them deepest, and the same argument repeats on the smaller problem. This is the same proof as Huffman coding.

Watch out for

  • One rope or an empty list costs 0. The size() > 1 loop handles both.
  • Do not assume the new rope is always tied next. With [5, 5, 6, 6], the two 6s go before the 10.
  • Sorting once at the start is not enough. New ropes must be placed back in order, which is what the heap does.
  • The cost of the last knot is the sum of all ropes. Do not forget to count it.

What interviewers ask next

  • Prove that shortest first is optimal. Use the knot tree: cost is length times depth, so the shortest ropes belong deepest, and swapping can only help. It is Huffman's argument.
  • Can you do it faster if the ropes come sorted? Yes, with two queues: one for the original sorted ropes and one for the new ropes, which are created in increasing order. Always take the smaller front. That is O(n) after sorting.
  • What if you may tie three ropes at once? Same greedy with the three shortest. If (n - 1) is not divisible by 2, add zero length ropes first so every knot has three.
  • Merge files with minimum cost, or Huffman codes? Same problem with different names.

Remember this

Grown-up words: the basket is a min heap (priority queue), always tying the two shortest is a greedy choice, the knot tree is a Huffman tree, and paying once per knot above a rope is the weighted depth of a leaf.