Six structural tests determine whether a problem is in the cheap category. Greedy with exchange argument — does picking the best local choice provably lead to optimal? (Many sorting, scheduling, allocation problems.) Dynamic programming — is there a small set of subproblems that combine into the answer? (Most counting, optimization, alignment problems.) Network flow — does the problem reduce to "maximum flow through a network with capacities"? (Many matching, allocation, partition problems.) Two-piece constraints — are all the constraints expressible as "if X then Y" with exactly two variables? (Many configuration, scheduling problems.) Linear programming — are constraints and objective linear with continuous variables? (Most operational planning.) Linear algebra — does the problem reduce to matrix factorization, eigenvalues, or linear systems? (Many ranking, recommendation, signal processing problems.) If any test passes, you have a settled problem and a library. Run the checklist in the meeting.
37
Is this in the cheap category — a checklist
Before you size an engineering effort, run the six tests. Half the time the problem is already solved.
- The six tests as an enum the codebase carries. Each branch points at the right crate. Wire this into your planning template and the procurement question becomes a constructor argument, not a debate.
enum CheapTest { GreedyWithExchange, // sorting, scheduling, MST DynamicProgramming, // counting, alignment, DAGs NetworkFlow, // matching, allocation, partition TwoPieceConstraint, // 2-SAT via SCC LinearProgramming, // continuous vars + linear constraints LinearAlgebra, // matrices, eigenvalues, factorization } fn crate_for(test: CheapTest) -> &'static str { match test { CheapTest::GreedyWithExchange => "std::slice::sort_unstable + a loop", CheapTest::DynamicProgramming => "hand-rolled table", CheapTest::NetworkFlow => "petgraph::algo::ford_fulkerson", CheapTest::TwoPieceConstraint => "petgraph::algo::tarjan_scc on implication graph", CheapTest::LinearProgramming => "good_lp with the HiGHS backend", CheapTest::LinearAlgebra => "nalgebra or faer", } } -
enumis Rust's tagged union — a single type whose values can be one of several named variants. Page 9'sOptionandResultwere enums with data-carrying variants (Some(T)holding a value,Err(E)holding an error). This page'sCheapTestis the simpler form — every variant is a unit variant, carrying no payload. Each variant is just a name that the type system treats as distinct. Compared to a plain integer or string constant, an enum gets you the exhaustiveness check (page 9 again): the compiler refuses to compile amatchthat doesn't cover every variant, so adding a seventh test means the compiler points at everymatchin the codebase that needs updating. That refactor-safety is the whole reason to reach for an enum instead of a string.// Unit variants — no payload, just a name. enum Direction { North, South, East, West } // Data-carrying variants — each variant holds different data. enum Shape { Circle(f64), // tuple-style: one f64 (radius) Rect(f64, f64), // tuple-style: width, height Point, // unit variant — no data Annotated { name: String, kind: u8 }, // struct-style: named fields } // Build a variant by writing its full path: let d = Direction::East; let s = Shape::Circle(3.0); // Why an enum and not a string constant or integer? // // 1. Exhaustiveness — match arms get checked at compile time. // Add a variant → compiler points at every match that needs updating. // // 2. Type safety — you can't accidentally pass a Shape where a // Direction is expected; the types are distinct. // // 3. Pattern matching — destructure data-carrying variants in one step. // Strings can't do that. // Page 37 uses pure unit variants — the checklist categories. // The match in `crate_for` covers all six, and the compiler will // refuse to compile if a seventh category gets added without a // corresponding arm. // One last note — enums under the hood: // // Rust stores an enum as a discriminant (small integer tag) plus // enough space for the largest variant's data. Unit-only enums // compile down to just the tag — `size_of::<CheapTest>() == 1` byte. // Data-carrying enums are bigger. Either way, dispatch on the // variant is a single comparison, not a function call or a hash.