-
-
Save Saarth-Jain/a8b16b0ea04d7c6b1dbb37cd7bb3b037 to your computer and use it in GitHub Desktop.
| #include <cs50.h> | |
| #include <stdio.h> | |
| #include <stdlib.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]; | |
| bool lock = true; | |
| // 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); | |
| int comparator(const void *a, const void *b); | |
| void lock_pairs(void); | |
| void print_winner(void); | |
| 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 and the preferences array from garbage values | |
| for (int i = 0; i < candidate_count; i++) | |
| { | |
| for (int j = 0; j < candidate_count; j++) | |
| { | |
| locked[i][j] = false; | |
| preferences[i][j] = 0; | |
| } | |
| } | |
| pair_count = 0; | |
| int voter_count = get_int("Number of voters: "); | |
| // 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[]) | |
| { | |
| for (int i = 0; i < candidate_count; i++) | |
| { | |
| if (strcmp(name, candidates[i]) == 0) | |
| { | |
| ranks[rank] = i; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| // Update preferences given one voter's ranks | |
| void record_preferences(int ranks[]) | |
| { | |
| for (int i = 0; i < candidate_count; i++) | |
| { | |
| for (int j = i + 1; j < candidate_count; j++) | |
| { | |
| preferences[ranks[i]][ranks[j]]++; | |
| } | |
| } | |
| } | |
| // Record pairs of candidates where one is preferred over the other | |
| void add_pairs(void) | |
| { | |
| 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[i][j] < preferences[j][i]) | |
| { | |
| pairs[pair_count].winner = j; | |
| pairs[pair_count].loser = i; | |
| pair_count++; | |
| } | |
| } | |
| } | |
| } | |
| // function used for sort | |
| int comparator(const void *a, const void *b) | |
| { | |
| pair *ab = (pair *)a; | |
| pair *ba = (pair *)b; | |
| // uses pointers to access the preferences and check how much a candidate wins over another | |
| return (preferences[ba->winner][ba->loser] - preferences[ab->winner][ab->loser]); | |
| } | |
| // Sort pairs in decreasing order by strength of victory | |
| void sort_pairs(void) | |
| { | |
| qsort(pairs, pair_count, sizeof(pair), comparator); | |
| } | |
| bool has_cycle(int winner, int loser) | |
| { | |
| while (winner != -1 && winner != loser) | |
| { | |
| bool found = false; | |
| for (int i = 0; i < candidate_count; i++) | |
| { | |
| if (locked[i][winner]) | |
| { | |
| found = true; | |
| winner = i; | |
| } | |
| } | |
| if (!found) | |
| { | |
| winner = -1; | |
| } | |
| } | |
| if (winner == loser) | |
| { | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Lock pairs into the candidate graph in order, without creating cycles | |
| void lock_pairs(void) | |
| { | |
| //TODO | |
| for (int i = 0; i < pair_count; i++) | |
| { | |
| if (!has_cycle(pairs[i].winner, pairs[i].loser)) | |
| { | |
| locked[pairs[i].winner][pairs[i].loser] = true; | |
| } | |
| } | |
| } | |
| // Print the winner of the election | |
| void print_winner(void) | |
| { | |
| //TODO | |
| for (int col = 0; col < MAX; col++) | |
| { | |
| bool found_source = true; | |
| for (int row = 0; row < MAX; row++) | |
| { | |
| if (locked[row][col] == true) | |
| { | |
| found_source = false; | |
| break; | |
| } | |
| } | |
| if (found_source) | |
| { | |
| printf("%s\n", candidates[col]); | |
| return; | |
| } | |
| } | |
| return; | |
| } |
Although this code passes all the checks made on CS50, it seems to me that the has_cycle function is incomplete. Meaning it misses some cases where there is a cycle. Let me show with an example:
Imagine there are 4 candidates: A, B, C and D (for simplicity). The ordered pairs are (represented as {winner, loser}):
- {A, D}
- {A, B}
- {C, B}
- {D, C}
- {B, D}
- {A, C}
If you draw this simple diagram, you'll see that the fifth pair, {B, D}, is a pair that cannot be drawn, because it would create the cycle B -> D -> C -> B.
Even though that is the case, the function has_cycle seems to miss this case, and incorrectly returns false (meaning there is no cycle formed by adding the pair {B, D}.
It seems to me that the proper way to write such a function would be by using recursion. Although I could not figure out how to write it yet.
Please let me know if I am missing something or if someone figured out how to write this function using recursion.
Hi,
I'm not sure about whether my answer would be suitable to solve your doubt regarding 6 votes for the 4 candidates thing, yet I have figured out the recursive version of has_cycle()...
bool has_cycle(int original_winner, int current_loser)
{
if (current_loser == original_winner)
return true;
for (int i = 0; i < pair_count; ++i)
{
if (pairs[i].winner == current_loser && locked[pairs[i].winner][pairs[i].loser])
{
if (has_cycle(original_winner, pairs[i].loser))
return true;
}
}
return false;
}We are given a winner and a loser, original_winner and current_loser respectively, I'd talk about the base case later.
We'll make a recursive call has_cycle(original_winner /* this doesn't change */, pair[i].loser) for each new pair in pairs, where the current_loser is the winner, if that pair already has a edge locked in.
Now in the recursive call to has_cycle, if the original_winner that we passed in while calling the function in lock_pairs() is the same as the current_loser in the recursive call, this means we have circled around, thus a cycle exists, and we return true.
If no cycle exists, it returns false.
Test for some randomized testcases at this website, where the graph has a red edge, and do a dry-run of the above recursive algorithm on that graph. That might help you understand it better.
Hope the answer helped :)
Thanks :)
@chu999 it just break the loop. Meaning that there is no cycle on locking that relation