[CS50] 04-2 Problem Set

6 minute read

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