Once upon a time in the land of Cambridge, Massachusetts, a weary student named Alex sat before a glowing screen, haunted by the legendary monster of Problem Set 3: . The Call to Battle
Alex had conquered the simple Runoff election, but Tideman was a different beast. The CS50 curriculum demanded a more sophisticated winner—a candidate who could win head-to-head battles without creating an infinite loop of confusion. The Strategy To defeat the beast, Alex had to master several weapons:
The Preferences: Alex collected the ranks of every voter, tallying how many people preferred Alice over Bob.
The Pairs: Every possible matchup was listed. Alice vs. Bob, Bob vs. Charlie, Charlie vs. Alice.
The Sorting: Alex used a "Strength of Victory" spell, sorting the pairs so the most landslide wins came first. The Locked Trap
Then came the hardest part: locking the pairs. Alex had to draw arrows from winners to losers. But there was a curse—if an arrow created a "cycle" (where Alice beats Bob, Bob beats Charlie, and Charlie beats Alice), the arrow could not be drawn.
Alex spent three days staring at a "No Cycle" function, battling the dark magic of Recursion. "How do I know if I'm pointing back to where I started?" Alex cried out. After many mugs of coffee and failed check50 runs, the logic clicked. To see if an arrow from A to B would create a cycle, Alex had to check if B already had a path leading back to A. The Source of Victory
Once the arrows were locked, the answer was revealed. The winner was the Source—the one candidate who had arrows pointing at others but no arrows pointing at themselves. Cs50 Tideman Solution
As the terminal finally flashed green with the words All tests passed!, Alex didn't just find a solution; they had earned the right to call themselves a programmer. (CS50) TIDEMAN - PROBLEM SET 3 | SOLUTION
Introduction
Tideman is a voting system implemented in the CS50 course, where voters rank candidates in order of preference. The goal of the Tideman solution is to determine the winner of an election based on the ranked ballots. In this report, we will outline the problem, provide a high-level overview of the solution, and walk through the implementation.
Problem Statement
The Tideman problem involves implementing a voting system that takes in a list of voters, candidates, and ranked ballots. The system must then determine the winner of the election based on the following rules:
Solution Overview
The Tideman solution involves the following steps: Once upon a time in the land of
Implementation
The implementation involves the following functions:
sort_pairs FunctionSort pairs in descending order of victory margin: margin = preferences[winner][loser] - preferences[loser][winner].
You can use any stable sorting algorithm. Bubble sort is fine for small candidate counts.
void sort_pairs(void)
for (int i = 0; i < pair_count - 1; i++)
for (int j = 0; j < pair_count - i - 1; j++)
int margin1 = preferences[pairs[j].winner][pairs[j].loser] - preferences[pairs[j].loser][pairs[j].winner];
int margin2 = preferences[pairs[j+1].winner][pairs[j+1].loser] - preferences[pairs[j+1].loser][pairs[j+1].winner];
if (margin1 < margin2)
pair temp = pairs[j];
pairs[j] = pairs[j+1];
pairs[j+1] = temp;
return;
Let’s implement lock_pairs and its helper creates_cycle.
vote FunctionThis function is identical to the one in plurality. It should record the voter’s rank for each candidate.
Logic: If the candidate name is found, set ranks[rank] = candidate_index and return true. Else false. The candidate with the fewest first-place votes is
bool vote(int rank, string name, int ranks[])
for (int i = 0; i < candidate_count; i++)
if (strcmp(name, candidates[i]) == 0)
ranks[rank] = i;
return true;
return false;
add_pairs FunctionWe need to populate the global pairs array. A pair exists if preferences[i][j] > preferences[j][i]. If equal (tie), skip.
Remember: The problem requires that each pair appears only once, with the winner first.
void add_pairs(void)
pair_count = 0;
for (int i = 0; i < candidate_count; i++)
for (int j = i + 1; j < candidate_count; j++)
if (preferences[i][j] > preferences[j][i])
pairs[pair_count].winner = i;
pairs[pair_count].loser = j;
pair_count++;
else if (preferences[j][i] > preferences[i][j])
pairs[pair_count].winner = j;
pairs[pair_count].loser = i;
pair_count++;
// ties are ignored
return;
creates_cycle Helper (Depth-First Search)This function checks if there is a path from loser to winner.
// Returns true if locking winner->loser would create a cycle bool creates_cycle(int winner, int loser) // If loser directly points to winner, cycle is immediate if (loser == winner) return true;// Check all candidates to see if loser points to someone, // and that someone eventually points to winner for (int i = 0; i < candidate_count; i++) if (locked[loser][i]) // If loser has an edge to i creates_cycle(i, winner)) return true; return false;
How it works:
loser is directly locked over winner, we have a cycle.i that loser beats (already locked), check if i can reach winner.