Cs50 Tideman Solution May 2026

The trick is realizing you don’t check cycles in the new edge itself. You check for existing paths.

Visual example:

Your code sees: Charlie → ? → Alice? It checks: does Charlie beat anyone locked? Yes, Charlie beats nobody yet in locked. Wait — we check recursively.

Actually, step through:

That’s wrong — wait! That would be a cycle (Charlie → Alice when Alice → Bob → Charlie already locked). Did we miss it?

Important correction: My earlier simple example fails because the cycle is three edges. The recursion must start from loser and try to reach winner. But in our state:

Locked: Alice→Bob, Bob→Charlie. Testing Charlie→Alice: Start from loser (Alice? No! loser is the second arg? Careful.)

Let’s rename parameters for clarity:

bool creates_cycle(int from, int to)  // We want to lock from->to
// If 'from' already beats 'to' indirectly
    for (int i = 0; i < candidate_count; i++)
if (locked[from][i])
return false;

Then in lock_pairs:

if (!creates_cycle(winner, loser))
    locked[winner][loser] = true;

Now test: Locked: Alice→Bob, Bob→Charlie. Check Charlie→Alice:

The fix: We must check the reverse direction. A cycle occurs if there is a path from loser to winner using existing locked edges before adding winner→loser.

So the correct helper:

bool will_create_cycle(int winner, int loser)
// If loser can reach winner already, then adding winner->loser creates cycle
    return can_reach(loser, winner);

bool can_reach(int from, int target) if (from == target) return true; for (int i = 0; i < candidate_count; i++) if (locked[from][i] && can_reach(i, target)) return true; return false;

Now test again: Locked: Alice→Bob, Bob→Charlie. Check Charlie→Alice: can_reach(Alice, Charlie)? No (Alice beats Bob, Bob beats Charlie, so yes! Alice can reach Charlie). Wait, that’s not what we check.

We check can_reach(loser, winner) = can_reach(Alice, Charlie)? Yes → cycle detected. Correct!

So the final correct version:

bool can_reach(int from, int target)
if (from == target)
        return true;
    for (int i = 0; i < candidate_count; i++)
if (locked[from][i] && can_reach(i, target))
            return true;
return false;

bool will_create_cycle(int winner, int loser) return can_reach(loser, winner);

void lock_pairs(void) for (int i = 0; i < pair_count; i++) int w = pairs[i].winner; int l = pairs[i].loser; if (!will_create_cycle(w, l)) locked[w][l] = true;

Once the graph is locked, the winner is the candidate with no incoming edges (the source of the graph).

This is the hardest part. You must detect if adding a new edge from winner to loser would create a cycle in the locked graph.

A cycle exists if there’s already a path from loser back to winner. Write a recursive function:

bool creates_cycle(int start, int current)
if (start == current)
        return true;
    for (int i = 0; i < candidate_count; i++)
        if (locked[current][i] && creates_cycle(start, i))
            return true;
    return false;

Before locking a pair (w, l), check: if (!creates_cycle(w, l)) then locked[w][l] = true.

The Tideman method prioritizes the strongest victories. Therefore, the array of pairs must be sorted in descending order based on the margin of victory. Cs50 Tideman Solution

If you want, I can:


Before touching code, you must understand the three core stages:

The winner is the candidate who ends up at the “source” of the locked graph (no incoming edges).

Run the provided tideman tests from CS50’s check50:

check50 cs50/problems/2024/x/tideman

To manually test, try a 3-candidate case where a cycle would occur:


We assume pairs is already sorted (by sort_pairs).

void lock_pairs(void)
// Iterate over each pair (strongest to weakest)
    for (int i = 0; i < pair_count; i++)
int winner = pairs[i].winner;
        int loser = pairs[i].loser;
    // If locking this pair does NOT create a cycle
    if (!creates_cycle(winner, loser))
locked[winner][loser] = true;
// Else: skip locking this pair

That’s it. The recursion in creates_cycle handles all chain connections.