Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Hard Problems & Adversarial Search  ›  Lesson

Reductions

Reductions 32 minTwo hard problems can be the same problem in disguise: a set S is independent exactly when everyone else is a vertex cover
You're building a piece ofMeridian — the bid desk
This piece — smallest_conflict_resolution(): Two ways of reading the same bidder-conflict graph: the largest group that can all be awarded together, and the smallest group that has to be excluded. Solving one hands you the other for free.
Scenario Some bidder pairs on Meridian's tender can't both be awarded the same route slot. The bid desk wants the largest group it can award with no conflicts, AND the smallest group it has to turn away to make every conflict go away — and it turns out solving either one hands you the other for free.
Your task
Build smallest_conflict_resolution(bidders, conflicts). conflicts is a list of [a, b] pairs that can't both be awarded. Brute force the largest independent set (try every subset size from len(bidders) down to 0, keep the first subset with no conflicting pair fully inside it), then return {"independent_set": ..., "vertex_cover": ...} where vertex_cover is every bidder NOT in that independent set, both sorted. Example: bidders ["a","b","c","d"], conflicts [["a","b"],["b","c"],["c","d"]] -> independent_set ["a","c"], vertex_cover ["b","d"].

Reductions

In short: a reduction shows that two problems are secretly the same problem. If you can turn any instance of problem A into an instance of problem B, solve B, and turn B's answer back into an answer for A, then A is "no harder than" B — and if the reverse translation also exists, they're exactly as hard as each other. This lesson builds one concretely: a group of bidders with no conflicts between them (an independent set) and the group left over once you remove it (a vertex cover) are the same object seen from two directions. Solve either search and the other answer falls out for free, with nothing left to compute.

A chain of four bidders acme-bolt-crane-delta; acme and crane circled green as the independent set, bolt and delta circled red as the vertex cover, with a complement arrow looping between the two boxes

Two conflicting bidders can't both win

Meridian's bid desk is handed a tender where some bidder pairs can't both be awarded — maybe they'd need the same depot slot at the same hour. Draw a node per bidder and an edge per conflict:

watch it move · graph

Four bidders, three conflicts. Which group can all be awarded together, and which group has to be turned away to clear every conflict?

Try acme and crane together. The edges acme-bolt and bolt-crane don't connect two chosen nodes, and crane-delta doesn't either — no conflict lands inside this group. It's independent. 4 nodes, 3 edges, undirected. Settled: acme, crane.

1 / 2

An independent set and a vertex cover of the same graph are complements of each other — every bidder is in exactly one of the two groups:

What does this print?
def is_independent_set(bidders, conflicts, group):
    group_set = set(group)
    return all(not (a in group_set and b in group_set) for a, b in conflicts)

def is_vertex_cover(bidders, conflicts, cover):
    cover_set = set(cover)
    return all(a in cover_set or b in cover_set for a, b in conflicts)

bidders = ["acme", "bolt", "crane", "delta"]
conflicts = [("acme", "bolt"), ("bolt", "crane"), ("crane", "delta")]
group = ["acme", "crane"]
cover = [b for b in bidders if b not in group]
print(is_independent_set(bidders, conflicts, group), is_vertex_cover(bidders, conflicts, cover))

Brute force, both directions, same answer

Searching for the biggest independent set and searching for the smallest vertex cover are the same exponential search, just read in opposite directions — try every subset, check a condition, stop at the first success:

python

Neither direction is secretly faster. Proving A reduces to B is never a promise that either one got easier — it's a promise that they rise and fall together. If someone ever found a fast algorithm for vertex cover, the same reduction would hand you a fast algorithm for independent set, for free, by just complementing the answer.

broken — fix it

This should return the complement of ["a", "c"] within ["a", "b", "c"], which is ["b"]. It currently returns ["a", "c"] itself. Flip the condition.

Quick practice

What does this print?
bidders = ["x", "y", "z"]
conflicts = [("x", "y"), ("y", "z")]
group = ["x", "z"]
cover = [b for b in bidders if b not in group]
print(sorted(cover))
What does this print?
def both_sides_agree(bidders, conflicts, group):
    cover = [b for b in bidders if b not in group]
    group_set, cover_set = set(group), set(cover)
    return len(group_set) + len(cover_set) == len(bidders)

print(both_sides_agree(["a", "b", "c", "d"], [], ["a", "b"]))

🎯 Your turn

Build smallest_conflict_resolution(bidders, conflicts). Brute force the largest independent set by trying subset sizes from biggest to smallest and keeping the first one where no conflict pair lands fully inside it. Then return {"independent_set": ..., "vertex_cover": ...}, where vertex_cover is every bidder not in that set — both sorted.

  • smallest_conflict_resolution(["a","b","c","d"], [["a","b"],["b","c"],["c","d"]]) → independent_set ["a", "c"], vertex_cover ["b", "d"].

Hint: itertools.combinations(bidders, size) tried from len(bidders) down to 0 finds the largest valid group first. The cover is just "everyone else."

Press ▶ Run, try the Live App chips, then ✓ Check.

Practice — level up

0/5 solved

Solve each in its own editor — Run to try, Check to grade. Stuck? Reveal a Hint, or the full Solution + walkthrough.

Warm-upCan this whole group be awarded together?

Write is_independent_set(bidders, conflicts, group), returning True if no pair in conflicts has both its bidders inside group. is_independent_set(["a","b","c"], [["a","b"]], ["a","c"]) -> True.

intractability
DrillDoes this group touch every conflict?

Write is_vertex_cover(bidders, conflicts, cover), returning True if every pair in conflicts has at least one bidder inside cover. is_vertex_cover(["a","b","c"], [["a","b"],["b","c"]], ["b"]) -> True.

intractability
BuildEveryone not in the group

Write complement(bidders, group), returning every bidder in bidders that is not in group, keeping bidders' original order. complement(["a","b","c"], ["b"]) -> ["a","c"].

intractability
BossTranslate one answer into the other

Write cover_from_independent_set(bidders, conflicts, group), returning the sorted vertex cover that group's complement forms IF group really is an independent set, or None if it isn't. cover_from_independent_set(["a","b","c"], [["a","b"]], ["a","c"]) -> ["b"].

intractabilityreduction
CapstoneThe smallest cover, found directly

Write brute_force_min_cover(bidders, conflicts), returning the size of the smallest vertex cover, found by trying subset sizes from 0 upward and testing each with is_vertex_cover. brute_force_min_cover(["a","b","c","d"], [["a","b"],["b","c"],["c","d"]]) -> 2.

intractabilityreduction
smallest_conflict_resolution.py
Call the function you wrote — just like your app's frontend would. Edit the inputs and hit Call.
smallest_conflict_resolution(bidders, conflicts) → dictThe largest group of bidders with no conflicts between them, and the smallest group left over that has to be excluded.
try:
returned
Press Run to execute your code.
Press Check to run the tests for this lesson.