ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CS50 Week3: Problem Set, Runoff
    Programming/CS50 2023. 7. 8. 17:14

    하버드 CS50 강의 3주차 Problem Set 과제 Runoff 의 풀이 과정을 다룹니다.
    C언어로 이차원 배열을 순회하고, 구조체로 구현된 선거 후보자 데이터를 다루는 문제입니다.

    Intro

    즉석결선투표제를 구현하는 문제

    ./runoff Alice Bob Charlie
    Number of voters: 5
    Rank 1: Alice
    Rank 2: Bob
    Rank 3: Charlie
    
    Rank 1: Alice
    Rank 2: Charlie
    Rank 3: Bob
    
    Rank 1: Bob
    Rank 2: Charlie
    Rank 3: Alice
    
    Rank 1: Bob
    Rank 2: Alice
    Rank 3: Charlie
    
    Rank 1: Charlie
    Rank 2: Alice
    Rank 3: Bob
    
    Alice

    Template

    이번에도 코드 템플릿이 주어져 있다.
    함수를 채워넣기만 하면 된다.

    #include <stdio.h>
    #include "cs50.h"
    
    // Max voters and candidates
    #define MAX_VOTERS 100
    #define MAX_CANDIDATES 9
    
    // preferences[i][j] is jth preference for voter i
    int preferences[MAX_VOTERS][MAX_CANDIDATES];
    
    // Candidates have name, vote count, eliminated status
    typedef struct
    {
        string name;
        int votes;
        bool eliminated;
    }
    candidate;
    
    // Array of candidates
    candidate candidates[MAX_CANDIDATES];
    
    // Numbers of voters and candidates
    int voter_count;
    int candidate_count;
    
    // Function prototypes
    bool vote(int voter, int rank, string name);
    void tabulate(void);
    bool print_winner(void);
    int find_min(void);
    bool is_tie(int min);
    void eliminate(int min);
    
    int main(int argc, string argv[])
    {
        // Check for invalid usage
        if (argc < 2)
        {
            printf("Usage: runoff [candidate ...]\n");
            return 1;
        }
    
        // Populate array of candidates
        candidate_count = argc - 1;
        if (candidate_count > MAX_CANDIDATES)
        {
            printf("Maximum number of candidates is %i\n", MAX_CANDIDATES);
            return 2;
        }
        for (int i = 0; i < candidate_count; i++)
        {
            candidates[i].name = argv[i + 1];
            candidates[i].votes = 0;
            candidates[i].eliminated = false;
        }
    
        voter_count = get_int("Number of voters: ");
        if (voter_count > MAX_VOTERS)
        {
            printf("Maximum number of voters is %i\n", MAX_VOTERS);
            return 3;
        }
    
        // Keep querying for votes
        for (int i = 0; i < voter_count; i++)
        {
    
            // Query for each rank
            for (int j = 0; j < candidate_count; j++)
            {
                string name = get_string("Rank %i: ", j + 1);
    
                // Record vote, unless it's invalid
                if (!vote(i, j, name))
                {
                    printf("Invalid vote.\n");
                    return 4;
                }
            }
    
            printf("\n");
        }
    
        // Keep holding runoffs until winner exists
        while (true)
        {
            // Calculate votes given remaining candidates
            tabulate();
    
            // Check if election has been won
            bool won = print_winner();
            if (won)
            {
                break;
            }
    
            // Eliminate last-place candidates
            int min = find_min();
            bool tie = is_tie(min);
    
            // If tie, everyone wins
            if (tie)
            {
                for (int i = 0; i < candidate_count; i++)
                {
                    if (!candidates[i].eliminated)
                    {
                        printf("%s\n", candidates[i].name);
                    }
                }
                break;
            }
    
            // Eliminate anyone with minimum number of votes
            eliminate(min);
    
            // Reset vote counts back to zero
            for (int i = 0; i < candidate_count; i++)
            {
                candidates[i].votes = 0;
            }
        }
        return 0;
    }
    
    // Record preference if vote is valid
    bool vote(int voter, int rank, string name)
    {
        // TODO
        return false;
    }
    
    // Tabulate votes for non-eliminated candidates
    void tabulate(void)
    {
        // TODO
        return;
    }
    
    // Print the winner of the election, if there is one
    bool print_winner(void)
    {
        // TODO
        return false;
    }
    
    // Return the minimum number of votes any remaining candidate has
    int find_min(void)
    {
        // TODO
        return 0;
    }
    
    // Return true if the election is tied between all candidates, false otherwise
    bool is_tie(int min)
    {
        // TODO
        return false;
    }
    
    // Eliminate the candidate (or candidates) in last place
    void eliminate(int min)
    {
        // TODO
        return;
    }

    Code

    vote 함수

    preferences 배열에 투표자의 선호도를 기록하면 됩니다.

    // Record preference if vote is valid
    bool vote(int voter, int rank, string name)
    {
        for (int i = 0; i < candidate_count; i++)
        {
            if (strcmp(candidates[i].name, name) == 0)
            {
                preferences[voter][rank] = i; // Record the candidate's index in the preferences array
                return true;
            }
        }
        return false;
    }

    tabulate 함수

    i 번째 투표자의 선호도가 가장 높으면서 아직 탈락하지 않은 후보에게 투표를 기록합니다.
    while문을 사용했습니다.

    // Tabulate votes for non-eliminated candidates
    void tabulate(void)
    {
        // Loop through each voter
        for (int i = 0; i < voter_count; i++)
        {
            // Loop through each voter's preference(rank)
            int j = 0;
            while (candidates[preferences[i][j]].eliminated) // If candidate is eliminated, next preference is checked
            {
                j++;
            } 
            candidates[preferences[i][j]].votes++;
        }
        return;
    }

    print_winner 함수

    과반 이상의 득표를 받은 후보가 있는지 확인하고 출력.

    // Print the winner of the election, if there is one
    bool print_winner(void)
    {
        for (int i = 0; i < candidate_count; i++) 
        {
            if (candidates[i].votes > voter_count / 2 ) 
            {
                printf("%s", candidates[i].name);
                return true;
            }
        }
        return false;
    }

    find_min 함수

    실수로 대소비교 방향을 잘못잡아서 한 10분을 해맸다.

    // Return the minimum number of votes any remaining candidate has
    int find_min(void)
    {
        int min_vote_total = MAX_VOTERS;
        for (int i = 0; i < candidate_count; i++)
        {
            if (min_vote_total > candidates[i].votes && !candidates[i].eliminated) 
            {
                min_vote_total = candidates[i].votes;
            }
        }
        return min_vote_total;
    }

    is_tie 함수

    모든 후보 동점인지 확인

    // Return true if the election is tied between all candidates, false otherwise
    bool is_tie(int min)
    {
        for (int i = 0; i < candidate_count; i++)
        {
            if (!candidates[i].eliminated && candidates[i].votes != min)
            {
                return false;
            }
        }
        return true;
    }

    eliminate 함수

    마지막 후보 탈락

    // Eliminate the candidate (or candidates) in last place
    void eliminate(int min)
    {
        for (int i = 0; i < candidate_count; i++)
        {
            if (candidates[i].votes == min && !candidates[i].eliminated)
            {
                candidates[i].eliminated = true;
            }
        }
        return;
    }

    후기

    문제의 난이도가 엄청 높지는 않지만, 이전보다 코드 길이면서 간단한 실수도 찾아내는 게 힘들어짐을 느꼈다.

    덕분에 VSC 디버거 사용이 조금 익숙해졌다....

    댓글