[CS50] 04-2 Problem Set
Published:
In this post, the solution for the problem set of the fourth lecture of CS50 is summarized.
Problem Set
tideman
You may refer to https://en.wikipedia.org/wiki/Ranked_pairs or https://cs50.harvard.edu/x/2024/psets/3/tideman/.
Answer
#include <cs50.h>
#include <stdio.h>
#include <string.h>
// Max number of candidates
#define MAX 9
// preferences[i][j] is number of voters who prefer i over j
int preferences[MAX][MAX];
// locked[i][j] means i is locked in over j
bool locked[MAX][MAX];
// Each pair has a winner, loser
typedef struct
{
int winner;
int loser;
} pair;
// Array of candidates
string candidates[MAX];
pair pairs[MAX * (MAX - 1) / 2];
int pair_count;
int candidate_count;
// Function prototypes
bool vote(int rank, string name, int ranks[]);
void record_preferences(int ranks[]);
void add_pairs(void);
void sort_pairs(void);
void lock_pairs(void);
void print_winner(void);
// added by me
bool cycle(int, int);
int isWinner(int);
void mergeSort(pair [], int, int);
void merge(pair[], int, int, int);
int main(int argc, string argv[])
{
// Check for invalid usage
if (argc < 2)
{
printf("Usage: tideman [candidate ...]\n");
return 1;
}
// Populate array of candidates
candidate_count = argc - 1;
if (candidate_count > MAX)
{
printf("Maximum number of candidates is %i\n", MAX);
return 2;
}
for (int i = 0; i < candidate_count; i++)
{
candidates[i] = argv[i + 1];
}
// Clear graph of locked in pairs
for (int i = 0; i < candidate_count; i++)
{
for (int j = 0; j < candidate_count; j++)
{
locked[i][j] = false;
}
}
pair_count = 0;
int voter_count = get_int("Number of voters: ");
// initialize preferences by me
for (int i = 0; i < MAX; i++){
for (int j = 0; j < MAX; j++){
preferences[i][j] = 0;
}
}
// Query for votes
for (int i = 0; i < voter_count; i++)
{
// ranks[i] is voter's ith preference
int ranks[candidate_count];
// Query for each rank
for (int j = 0; j < candidate_count; j++)
{
string name = get_string("Rank %i: ", j + 1);
if (!vote(j, name, ranks))
{
printf("Invalid vote.\n");
return 3;
}
}
record_preferences(ranks);
printf("\n");
}
add_pairs();
sort_pairs();
lock_pairs();
print_winner();
return 0;
}
// Update ranks given a new vote
bool vote(int rank, string name, int ranks[])
{
// TODO
for (int i = 0; i < candidate_count; i++){
if (!strcmp(candidates[i], name)){
ranks[rank] = i;
return true;
}
}
return false;
}
// Update preferences given one voter's ranks
void record_preferences(int ranks[])
{
// TODO
for (int i = 0; i < candidate_count; i++){
for (int j = 0; j < candidate_count; j++){
if (i < j){
preferences[ranks[i]][ranks[j]]++;
}
}
}
return;
}
// Record pairs of candidates where one is preferred over the other
void add_pairs(void)
{
// TODO
for (int i = 0; i < candidate_count; i++){
for (int j = i + 1; j < candidate_count; j++){
pair pair_tmp;
if (preferences[i][j] > preferences[j][i]){
pair_tmp.winner = i;
pair_tmp.loser = j;
//pair_tmp.vic_size = preferences[i][j] - preferences[j][i];
pairs[pair_count] = pair_tmp;
pair_count++;
}
else if (preferences[i][j] < preferences[j][i]){
pair_tmp.winner = j;
pair_tmp.loser = i;
//pair_tmp.vic_size = preferences[j][i] - preferences[i][j];
pairs[pair_count] = pair_tmp;
pair_count++;
}
}
}
return;
}
// Sort pairs in decreasing order by strength of victory
void sort_pairs(void)
{
// TODO
mergeSort(pairs, 0, pair_count - 1);
return;
}
// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs(void)
{
// TODO
for (int i = 0; i < pair_count; i++){
int winner = pairs[i].winner;
int loser = pairs[i].loser;
if (!cycle(winner, loser)){
locked[winner][loser] = true;
}
}
return;
}
bool cycle(int winner, int loser){
bool returnVal = false;
for (int i = 0; i < candidate_count; i++){
if (locked[loser][i]){
if (i == winner){
return true;
}
else{
returnVal = returnVal || cycle(winner, i);
}
}
}
return returnVal;
}
// Print the winner of the election
void print_winner(void)
{
// TODO
int winner_candidate = 0;
while (true){
int next_candidate = isWinner(winner_candidate);
if (next_candidate == winner_candidate){
printf("%s\n", candidates[winner_candidate]);
return;
}
else{
winner_candidate = next_candidate;
}
}
}
int isWinner(int winner_candidate){
for (int i = 0; i < candidate_count; i++){
if (winner_candidate != i && locked[i][winner_candidate]){
return i;
}
}
return winner_candidate;
}
void mergeSort(pair pairs_arr [], int l, int r){
int m = (l + r) / 2;
if (l == r){
return;
}
else{
mergeSort(pairs_arr, l, m);
mergeSort(pairs_arr, m + 1, r);
merge(pairs_arr, l, m, r);
return;
}
}
void merge(pair pairs_arr[], int l, int m, int r){
int l_idx = l;
int r_idx = m + 1;
pair temp[r - l + 1];
while((l_idx <= m) && (r_idx <= r)){
int vic_size_l = preferences[pairs_arr[l_idx].winner][pairs_arr[l_idx].loser] - preferences[pairs_arr[l_idx].loser][pairs_arr[l_idx].winner];
int vic_size_r = preferences[pairs_arr[r_idx].winner][pairs_arr[r_idx].loser] - preferences[pairs_arr[r_idx].loser][pairs_arr[r_idx].winner];
if (vic_size_l >= vic_size_r){
temp[l_idx + r_idx - (l + m + 1)] = pairs_arr[l_idx];
l_idx++;
}
else{
temp[l_idx + r_idx - (l + m + 1)] = pairs_arr[r_idx];
r_idx++;
}
}
if (l_idx <= m){
for (int i = l_idx; i <= m; i++){
temp[r_idx - (l + m + 1) + i] = pairs_arr[i];
}
}
else if(r_idx <= r){
for (int i = r_idx; i <= r; i++){
temp[l_idx - (l + m + 1) + i] = pairs_arr[i];
}
}
for (int i = 0; i < r - l + 1; i++){
pairs_arr[l + i] = temp[i];
}
}
There are two points related to the main lecture of this week. Firstly, I used mergesort algorithms to sort ‘Pairs’. Secondly, I used recursive function to check whether cycle exists in the candidate graph. The ‘cycle’ function that I wrote might be a bit confusing, so I searched better code using same recursive logic, but different expressions.
bool cycle(int winner, int loser){
if (winner == loser){
return true;
}
for (int i = 0; i < candidate_count; i++){
if (locked[loser][i]){
if (cycle(winner, i){
return true;
}
}
}
return false;
Solutions for ‘Sort’, ‘Plurality’, and ‘Runoff’ are not provided in this post since the core strategies to solve those problems are all included in ‘tideman’.

Leave a Comment