AlgoMaster Logo

Binary Tree Cameras

hardFrequency8 min readUpdated June 23, 2026

Understanding the Problem

We need to place the fewest cameras possible on a binary tree so that every node is "covered," meaning it either has a camera on it or is adjacent to a node with a camera. A camera covers the node it sits on, its parent, and its direct children.

This is the minimum dominating set problem restricted to binary trees: choose the smallest set of nodes such that every node is in the set or adjacent to a member. On general graphs that problem is NP-hard, but the tree structure makes it solvable in linear time. One observation drives every efficient solution: a camera on a leaf covers two nodes (the leaf and its parent), while a camera on the leaf's parent covers up to four (itself, both children, and its own parent). Whenever the tree has more than one node, some optimal placement puts every camera on an internal node.

Key Constraints:

  • 1 <= number of nodes <= 1000 - The tree is small, so the challenge is correctness rather than performance. Even an O(n^2) solution would finish in under a millisecond; only the exponential brute force is ruled out.
  • Node.val == 0 - The values do not matter. This is purely a structural problem about the tree's shape.

Approach 1: Brute Force (Try All Subsets)

Intuition

Try every possible subset of nodes as camera positions, check whether each subset covers all nodes, and return the size of the smallest valid subset.

Each node either gets a camera or it does not, which gives 2^n subsets. For each subset, we verify that every node is either a camera node or adjacent to one.

Algorithm

  1. Enumerate all 2^n subsets of nodes.
  2. For each subset, mark the selected nodes as having cameras.
  3. Check if every node in the tree is covered (has a camera or is adjacent to a camera node).
  4. Track the minimum subset size among all valid configurations.
  5. Return that minimum.

Example Walkthrough

1Tree has 4 nodes. Try all 2^4 = 16 subsets of camera placements.
0000
1/6

Code

This approach is exponential and unusable for the given constraints. The waste is that subsets are evaluated globally even though the problem decomposes: the best way to cover a subtree depends only on what happens at the subtree's root, not on the rest of the tree. Dynamic programming on the tree exploits that decomposition.

Approach 2: Tree DP (Three Costs per Node)

Intuition

The rest of the tree needs only one piece of information about a subtree: the status of its root. That status takes one of three forms, so we compute three costs for every node:

  • camera: the minimum cameras to cover the whole subtree with a camera placed on this node.
  • covered: the minimum cameras to cover the whole subtree with no camera here, meaning at least one child holds a camera.
  • uncovered: the minimum cameras to cover everything below this node while the node itself stays unmonitored, waiting for its parent.

A post-order DFS fills in these triples bottom-up using three transitions:

  • camera = 1 + the cheapest of the three states for each child. A camera here monitors both children from above, so each child may take any state, including uncovered.
  • covered = the cheaper of "left child takes a camera" and "right child takes a camera." The child without the camera must pay for its own coverage (camera or covered), because nothing at this node monitors it.
  • uncovered = left covered + right covered. No child may hold a camera (that would cover this node, contradicting the state) and no child may stay uncovered (nothing below or here will ever cover it).

A null child contributes (INF, 0, 0): it cannot hold a camera, and it costs nothing to leave alone. The answer is min(camera, covered) at the root. The root's uncovered cost is discarded because no parent exists to cover it.

Algorithm

  1. Run a post-order DFS that returns the triple (camera, covered, uncovered) for each node.
  2. For a null node, return (INF, 0, 0).
  3. Compute the node's three costs from the children's triples using the transitions above.
  4. Cap each cost at INF so the sentinel cannot grow without bound on deep trees.
  5. Return min(camera, covered) for the root.

Example Walkthrough

1Post-order DFS computes a triple (camera, covered, uncovered) per node. A null child counts as (INF, 0, 0).
0000
1/6

Code

This already runs in linear time, and the triple formulation generalizes: the same decomposition solves weighted variants where a camera costs a different amount at each node. In this problem every camera costs 1, and that uniformity supports a simpler rule that skips the cost comparisons entirely.

Approach 3: Greedy DFS (Three States)

Intuition

The DP carries three costs per node and compares sums of them at every step. Those comparisons turn out to be unnecessary. A camera is only ever needed when a child would otherwise stay unmonitored, so a single status per node, computed bottom-up, determines every decision. Each node reports to its parent exactly one of three states:

  • State 0 (NOT_COVERED): This node has no camera and is not covered by any child's camera. It needs its parent to place a camera.
  • State 1 (HAS_CAMERA): This node has a camera on it. It covers itself, its children, and its parent.
  • State 2 (COVERED): This node does not have a camera but is already covered by at least one child's camera.

Algorithm

  1. Define three states: NOT_COVERED = 0, HAS_CAMERA = 1, COVERED = 2.
  2. Run a post-order DFS on the tree.
  3. Treat null children as COVERED (they do not need monitoring and should not force a camera).
  4. At each node, apply the state transition rules based on children's states:
    • If any child is NOT_COVERED, place a camera on this node.
    • If any child HAS_CAMERA, this node is COVERED.
    • If both children are COVERED, this node is NOT_COVERED.
  5. After the DFS, if the root is NOT_COVERED, add one more camera for the root.
  6. Return the total camera count.

Example Walkthrough

1A chain of four nodes. Post-order DFS resolves the deepest node first.
0000
1/7

Code