diff --git a/dynamic-programming/notes.md b/dynamic-programming/notes.md
index dd6cfdbaab3bc541e13a950a315d1c5d4503691e..33d0f7d3739d7c68b972c6e4212aeb6fa7527d54 100644
--- a/dynamic-programming/notes.md
+++ b/dynamic-programming/notes.md
@@ -111,15 +111,15 @@ Problem: Wrap a paragraph to a line length, minimizing the sum of the squares of
 ## DAG
 
 *   Edges (actions):
-    *   …
+    *   Place a line on the page
 *   Vertices (situations):
-    *   …
+    *   The number of words placed so far
 *   Edge weights:
-    *   …
+    *   The penalty for the line just placed
 *   Topological order:
-    *   …
+    *   Increasing order: 0 → 1 → … → `wordCount`
 *   Goal:
-    *   …
+    *   Find a shortest path from 0 to `wordCount`
 
 ## Backpointer Class
 
diff --git a/dynamic-programming/src/features/knapsack/solver.js b/dynamic-programming/src/features/knapsack/solver.js
index ff780b5e39ca74a1f413a71705830c265adfd043..da85c9710487034c266dfcdf5486f86056f70a46 100644
--- a/dynamic-programming/src/features/knapsack/solver.js
+++ b/dynamic-programming/src/features/knapsack/solver.js
@@ -1,5 +1,7 @@
 import { Item } from './items.js';
 
+const KILOGRAM_OF_NOTHING = new Item(1, 0);
+
 class Backpointer {
   constructor(item, totalValue) {
     this.item = item;
@@ -9,7 +11,23 @@ class Backpointer {
 
 function chooseBackpointer(items, backpointers) {
   const currentWeight = backpointers.length;
-  // TODO: stub
+  let best = new Backpointer(
+    KILOGRAM_OF_NOTHING,
+    backpointers[currentWeight - 1].totalValue,
+  );
+  for (const item of items) {
+    const previousWeight = currentWeight - item.weight;
+    if (previousWeight >= 0) {
+      const candidate = new Backpointer(
+        item,
+        item.value + backpointers[previousWeight].totalValue,
+      );
+      if (candidate.totalValue > best.totalValue) {
+        best = candidate;
+      }
+    }
+  }
+  return best;
 }
 
 export function chooseItems(items, weightLimit) {
@@ -20,7 +38,10 @@ export function chooseItems(items, weightLimit) {
   }
   const reversedPath = [];
   for (let weight = weightLimit; weight > 0; weight -= backpointers[weight].item.weight) {
-    reversedPath.push(…);
+    const item = backpointers[weight].item;
+    if (item !== KILOGRAM_OF_NOTHING) {
+      reversedPath.push(item);
+    }
   }
   return reversedPath.reverse();
 }