I have a problem that I have been struggling with the check_pairs function for several days. Finally, I wrote a version of the code that in my opinion everything is correct and indeed in CS50 everything shows green except :( lock_pairs skips final pair if it creates cycle.
And I found this code:
// Recursive function to check if entry makes a circle
bool makes_circle(int cycle_start, int loser)
{
if (loser == cycle_start)
{
// If the current loser is the cycle start
// The entry makes a circle
return true;
}
for (int i = 0; i < candidate_count; i++)
{
if (locked[loser][i])
{
if (makes_circle(cycle_start, i))
{
// Forward progress through the circle
return true;
}
}
}
return false;
}
// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs()
{
for (int i = 0; i < pair_count; i++)
{
if (!makes_circle(pairs[i].winner, pairs[i].loser))
{
// Lock the pair unless it makes a circle
locked[pairs[i].winner][pairs[i].loser] = true;
}
}
}
Found it here: PSET 3 Tideman CS50 lock_pairs doesn't lock correctly
When I pasted it to tideman everything was green with check50 and I can't figure out why and how it differs from my function. I checked various combinations in debug50, drew a few pages and still don't understand what is the detail that makes this recursive function different from mine. Could someone explain to me step by step why this function is correct and mine is not?
My code:
void lock_pairs(void)
{
for (int i = 0; i < pair_count; i++)
{
bool cycle = false;
// check if the loser of the actual checking pair won with any other candidate
for (int j = 0; j < candidate_count && !cycle; j++)
{
if (locked[pairs[i].loser][j])
{
// if the loser beats any other candidate, check if this other candidate beats the winner of the actual checking pair
for (int k = 0; k < candidate_count && !cycle; k++)
{
if (locked[j][k])
{
if (k == pairs[i].winner)
{
cycle = true;
}
}
}
}
}
if (!cycle)
{
locked[pairs[i].winner][pairs[i].loser] = true;
}
}
return;
}
Related
After many failed attempts, I managed to used a recursive function to check the locked pairs.
When I check the results, both with the program and manually, I get the correct locked pairs. But I keep getting the "lock_pairs skips final pair if it creates cycle" error, while all other checks run ok.
I don't know how to see what's wrong here. Can you give me a hand?
I'm not asking for someone to tell me how to do it properly, but to point out where is my error, so I can think of the best way to work this out.
Here is my code:
bool find_connections(int i, int j);
int lock_done = 100;
void lock_pairs(void)
{
// Iterates through all the pairs
for (int i = 0; i < pair_count; i++)
{
// lock_done is used in the recursive function as indicator that the locked value is false, and every further process of the loop is unnecessary.
lock_done = 100;
// Calls find_connection function, and sends the index of the pair twice, one for checking the cycle (it remains constant) and one to be replaced by the losing candidate of the found pair.
int is_locked = find_connections(i, i);
locked[pairs[i].winner][pairs[i].loser] = is_locked;
}
return;
}
bool find_connections(int i, int j)
{
// Iterates through all pairs to check for a coincidence between loser of j and winner of r
for (int r = 0; r < pair_count; r++)
{
// If it was confirmed as false, it returns "false"
if (lock_done != 100)
return lock_done;
// Checks coincidence between loser of j and winner of r
if (pairs[j].loser == pairs[r].winner)
{
// When there's a match, if pair is locked and the loser connects to the original winner, then the lock condition is false.
if (pairs[i].winner == pairs[r].loser && locked[pairs[r].winner][pairs[r].loser] == 1)
{
lock_done = 0;
return 0;
}
// Otherwise, it calls the function again to check the connections starting with the new pair.
else if(locked[pairs[r].winner][pairs[r].loser] == 1)
find_connections(i, r);
}
}
// When all pairs are checked, if one was already found as false, then it returns false.
if (lock_done == 0)
return 0;
// Otherwise, there is no cycle, and the locked condition is true.
else
{
lock_done = 1;
return 1;
}
}
I'm working on the CS50x's Tideman problem
(https://cs50.harvard.edu/x/2022/psets/3/tideman/).
All the tests passed except one:
:( lock_pairs skips final pair if it creates cycle
lock_pairs did not correctly lock all non-cyclical pairs
I guess the problem is in my custom function has_circle that checks if there will be a circle in a graph if an edge is placed.
I would really be grateful for your help. I wrote the rest of the code fairly quickly (for a begginer) but this bug is just....
This is the code:
bool has_circle(int loser, int winner)
{
// base case, break
if (winner == loser)
{
return true;
}
// iterate through all pairs to see if our loser is a winner against someone
for (int i = 0; i < pair_count; i++)
{
if (locked[loser][i] == true)
{
// check if primary winner is a loser against i, if edge can be placed
return has_circle(i, winner);
}
}
return false;
}
// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs(void)
{
//
for (int i = 0; i < pair_count; i++)
{
// check for circle
if (!has_circle(pairs[i].loser, pairs[i].winner))
{
locked[pairs[i].winner][pairs[i].loser] = true;
}
}
return;
}
This line
return has_circle(i, winner);
always returns a value, ending the the loop and the recursion, the first time locked[loser][i] is true, regardless of the actual result of has_circle.
Instead, you want the loop to be able to continue if has_circle returns false, but break the loop and return true when has_circle returns true.
bool has_circle(int loser, int winner)
{
// base case, break
if (winner == loser)
{
return true;
}
// iterate through all pairs to see if our loser is a winner against someone
for (int i = 0; i < pair_count; i++)
{
if (locked[loser][i] && has_circle(i, winner))
{
return true;
}
}
return false;
}
I'm stuck at the lock_pairs function of Tideman. I already wrote the code for it and it looks good. It also behaves as it should when running it through the debugger; however, every time I try it with check50 it gives me an error on "lock pair skips final pair if it creates a cycle".
In order to try it out, I've used examples where the final pair is the one which should be skipped and everything goes as planned when I watch the whole process step by step on the debugger, so I really don't know what I'm missing here.
Here's the explanation of the problem: https://cs50.harvard.edu/x/2022/psets/3/tideman/
void lock_pairs(void)
{
// TODO
// When no_cycle is 0, it indicates that there is no cycle and that locking the pair is safe
int no_cycle = 0;
for (int i = 0; i < pair_count; i++)
{
for (int j = 0; j < pair_count; j++)
{
// We check each pair, looking for a locked pair where the loser of the pair we're checking is the winner.
if (!locked[pairs[i].loser][pairs[j].loser])
{
no_cycle = 0;
continue;
}
else
{
// If the result is true, we check for a possible cycle using the cycle_check function
if (cycle_check(i, j) == false)
{
// If the result is false, we lock the pair and move on
no_cycle = 0;
break;
}
else
{
// If the result is true (indicating a cycle), we change the value of no_cycle to avoid locking the pair
no_cycle = 1;
break;
}
}
}
if (no_cycle == 0)
{
locked[pairs[i].winner][pairs[i].loser] = true;
}
else continue;
}
return;
}
bool cycle_check(i, j)
{
// We check if the loser of the locked pair is the same as our pair's winner or starting point (the end of the chain is also the begining)
if (pairs[j].loser == pairs[i].winner)
{
return true;
}
else
{
//If it isn't, we go through each locked pair, following the chain which could lead to the cycle
for (int h = 0; h < pair_count; h++)
{
if (locked[pairs[j].loser][pairs[h].loser])
{
// Once we find the next link in the chain, we start over
j = h;
if (cycle_check(i, j) == true)
{
// Once we find that there is indeed a cycle, we break recursion
return true;
}
}
}
// If we follow the chain but don't find a cycle, the function returns false and the pair is locked
return false;
}
}
Been stuck forever on this. Any help would be much appreciated.
Btw, english is not my primary language, so please excuse any grammar errors.
i am new to this site so i might not ask my question correctly which i am sorry for but i have been struggling with PSET 3 Tideman for quite a while. Currently i am on lock_pairs and i dont know what to do. Would appreciate your help!
These are my prompts for check50:
:) lock_pairs locks all pairs when no cycles
:( lock_pairs skips final pair if it creates cycle
lock_pairs did not correctly lock all non-cyclical pairs
:( lock_pairs skips middle pair if it creates a cycle
lock_pairs did not correctly lock all non-cyclical pairs
void lock_pairs(void)
{
int winner;
int win_count[MAX];
int temp_win = 0;
for (int i = 0; i < pair_count; i++)
{
locked[ pairs[i].winner ][ pairs[i].loser ] = true;
win_count[ pairs[i].winner ]++;
if (win_count [ pairs[i].winner ] > temp_win )
{
winner = pairs[i].winner;
}
}
for ( int p = 0; p < pair_count; p++)
{
if (win_count[ pairs[p].winner ] == win_count[winner] && pairs[p].winner != winner )
{
for (int i = pair_count - 1; i < -1 ; i--)
{
locked[ pairs[i].winner ][ pairs[i].loser ] = false;
win_count [ pairs[i].winner ]--;
if (pairs[i].winner == winner )
{
win_count[winner]--;
for (int j = 0; j < pair_count; j++)
{
if (win_count[ pairs[j].winner ] > win_count[winner])
{
winner = pairs[j].winner;
}
if (win_count[ pairs[j].winner] == win_count[winner] && pairs[j].winner != winner)
{
break;
}
}
}
else
{
locked[ pairs[i].winner ][ pairs[i].loser ] = true;
}
}
}
}
return;
}
You may have misunderstood the logic required behind lock_pairs, because your function is overly complicated. This is what you're asked to do-
Go through each pair one by one, and lock them if necessary
How to know, if it is necessary? Simple, you make sure locking them does not constitute a circle
What does "constitute a circle" mean? This is explained very well by the course itself. More info right there
But basically,
How would this work in the case of the votes above? Well, the biggest margin of victory for a pair is Alice beating Bob, since 7 voters prefer Alice over Bob (no other head-to-head matchup has a winner preferred by more than 7 voters). So the Alice-Bob arrow is locked into the graph first. The next biggest margin of victory is Charlie’s 6-3 victory over Alice, so that arrow is locked in next.
Next up is Bob’s 5-4 victory over Charlie. But notice: if we were to add an arrow from Bob to Charlie now, we would create a cycle! Since the graph can’t allow cycles, we should skip this edge, and not add it to the graph at all. If there were more arrows to consider, we would look to those next, but that was the last arrow, so the graph is complete.
This step-by-step process is shown below, with the final graph at right.
Now the real deal, how do you put this in code?
Like this-
// Recursive function to check if entry makes a circle
bool makes_circle(int cycle_start, int loser)
{
if (loser == cycle_start)
{
// If the current loser is the cycle start
// The entry makes a circle
return true;
}
for (int i = 0; i < candidate_count; i++)
{
if (locked[loser][i])
{
if (makes_circle(cycle_start, i))
{
// Forward progress through the circle
return true;
}
}
}
return false;
}
Essentially, to check if locking in a winner->loser makes a circle, you walk back the entries you have locked starting from the loser and going towards all the candidates this loser has won against. If you reach the winner somewhere along the way, that's a circle if I've ever seen one.
Note that circle_start though, lest it gets confusing. That's only there to remember the original circle start (which is the winner in the main loop in lock_pairs). This is constant throughout the entire recursion. Remember, once again, our goal is to iterate through all the candidates loser has won against (and also have been locked) and make sure none of these are circle_start (aka the winner that we are about to lock in).
So, that function right there does all the heavy lifting, your lock_pairs function is gonna be extremely simple-
// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs()
{
for (int i = 0; i < pair_count; i++)
{
if (!makes_circle(pairs[i].winner, pairs[i].loser))
{
// Lock the pair unless it makes a circle
locked[pairs[i].winner][pairs[i].loser] = true;
}
}
}
This is a hackerrank problem.
Find out the maximum movies that can be watched. Assume the list to be distinct movies.
Input:
movieStart[] = {10,12,9,14,16,14}
movieEnd[] = {11,13,15,16,18,18}
Output : 4 (10-11,12-13,14-16,16-18)
Below code is giving me correct output. Would like to know the best solution possible for this problem.
Also what algorithm is used to solve this kind of problem?
static int getMaxMovies(int[] movie_start, int[] movie_end) {
int cnt = 0;
for (int i = 0; i < movie_start.length; i++) {
for (int j = 0; j < movie_start.length; j++) {
if (movie_start[j] == movie_start[i] && movie_end[j] == movie_end[i]) {
continue;
}
if (movie_start[j] >= movie_start[i] && movie_end[j] <= movie_end[i]) {
cnt += 1;
break;
}
}
}
return movie_start.length - cnt;
}
That's "Activity selection" problem - which could be easily solved with O(n) (in case of sorted input) or O(nlogn) (when input isn't sorted) greedy algorithm. If input is already sorted by finishing (end) time you should pick movies which doesn't conflict with previously selected movie.
Pseudocode:
Sort by finish times
int previouslySelectedIndex = 0;
int watchedMoviesCount = 1;
for (int i=1; i<movie_end.Length; i++)
{
if (movie_start[i] >= movie_end[previouslySelectedIndex)
{
watchedMoviesCount++;
previouslySelectedIndex=i;
}
}