From dc1a8de37a5f96010181289097d5761790b4bdc9 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Sun, 24 Apr 2022 05:08:49 +0900 Subject: [PATCH 1/6] request extra/distract-the-trainers --- extra/distract-the-trainers/constraints.txt | 21 ++++++++ extra/distract-the-trainers/readme.txt | 53 +++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 extra/distract-the-trainers/constraints.txt create mode 100644 extra/distract-the-trainers/readme.txt diff --git a/extra/distract-the-trainers/constraints.txt b/extra/distract-the-trainers/constraints.txt new file mode 100644 index 0000000..f201987 --- /dev/null +++ b/extra/distract-the-trainers/constraints.txt @@ -0,0 +1,21 @@ +Java +==== +Your code will be compiled using standard Java 8. All tests will be run by calling the solution() method inside the Solution class + +Execution time is limited. + +Wildcard imports and some specific classes are restricted (e.g. java.lang.ClassLoader). You will receive an error when you verify your solution if you have used a blacklisted class. + +Third-party libraries, input/output operations, spawning threads or processes and changes to the execution environment are not allowed. + +Your solution must be under 32000 characters in length including new lines and and other non-printing characters. + +Python +====== +Your code will run inside a Python 2.7.13 sandbox. All tests will be run by calling the solution() function. + +Standard libraries are supported except for bz2, crypt, fcntl, mmap, pwd, pyexpat, select, signal, termios, thread, time, unicodedata, zipimport, zlib. + +Input/output operations are not allowed. + +Your solution must be under 32000 characters in length including new lines and and other non-printing characters. diff --git a/extra/distract-the-trainers/readme.txt b/extra/distract-the-trainers/readme.txt new file mode 100644 index 0000000..4aa3353 --- /dev/null +++ b/extra/distract-the-trainers/readme.txt @@ -0,0 +1,53 @@ +Distract the Trainers +===================== + +The time for the mass escape has come, and you need to distract the bunny trainers so that the workers can make it out! Unfortunately for you, they're watching the bunnies closely. Fortunately, this means they haven't realized yet that the space station is about to explode due to the destruction of the LAMBCHOP doomsday device. Also fortunately, all that time you spent working as first a minion and then a henchman means that you know the trainers are fond of bananas. And gambling. And thumb wrestling. + +The bunny trainers, being bored, readily accept your suggestion to play the Banana Games. + +You will set up simultaneous thumb wrestling matches. In each match, two trainers will pair off to thumb wrestle. The trainer with fewer bananas will bet all their bananas, and the other trainer will match the bet. The winner will receive all of the bet bananas. You don't pair off trainers with the same number of bananas (you will see why, shortly). You know enough trainer psychology to know that the one who has more bananas always gets over-confident and loses. Once a match begins, the pair of trainers will continue to thumb wrestle and exchange bananas, until both of them have the same number of bananas. Once that happens, both of them will lose interest and go back to supervising the bunny workers, and you don't want THAT to happen! + +For example, if the two trainers that were paired started with 3 and 5 bananas, after the first round of thumb wrestling they will have 6 and 2 (the one with 3 bananas wins and gets 3 bananas from the loser). After the second round, they will have 4 and 4 (the one with 6 bananas loses 2 bananas). At that point they stop and get back to training bunnies. + +How is all this useful to distract the bunny trainers? Notice that if the trainers had started with 1 and 4 bananas, then they keep thumb wrestling! 1, 4 -> 2, 3 -> 4, 1 -> 3, 2 -> 1, 4 and so on. + +Now your plan is clear. You must pair up the trainers in such a way that the maximum number of trainers go into an infinite thumb wrestling loop! + +Write a function solution(banana_list) which, given a list of positive integers depicting the amount of bananas the each trainer starts with, returns the fewest possible number of bunny trainers that will be left to watch the workers. Element i of the list will be the number of bananas that trainer i (counting from 0) starts with. + +The number of trainers will be at least 1 and not more than 100, and the number of bananas each trainer starts with will be a positive integer no more than 1073741823 (i.e. 2^30 -1). Some of them stockpile a LOT of bananas. + +Languages +========= + +To provide a Python solution, edit solution.py +To provide a Java solution, edit Solution.java + +Test cases +========== +Your code should pass the following test cases. +Note that it may also be run against hidden test cases not shown here. + +-- Python cases -- +Input: +solution.solution(1,1) +Output: + 2 + +Input: +solution.solution([1, 7, 3, 21, 13, 19]) +Output: + 0 + +-- Java cases -- +Input: +solution.solution(1,1) +Output: + 2 + +Input: +Solution.solution([1, 7, 3, 21, 13, 19]) +Output: + 0 + +Use verify [file] to test your solution and see how it does. When you are finished editing your code, use submit [file] to submit your answer. If your solution passes the test cases, it will be removed from your home folder. -- 2.47.2 From d548438ea7bcc3663d09b5ff4307a87c02190894 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Sun, 24 Apr 2022 05:10:37 +0900 Subject: [PATCH 2/6] solve by emulate the game Verifying solution... Test 1 passed! Test 2 passed! Test 3 failed [Hidden] Test 4 failed [Hidden] Test 5 failed [Hidden] --- extra/distract-the-trainers/solution.py | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 extra/distract-the-trainers/solution.py diff --git a/extra/distract-the-trainers/solution.py b/extra/distract-the-trainers/solution.py new file mode 100644 index 0000000..a3f3918 --- /dev/null +++ b/extra/distract-the-trainers/solution.py @@ -0,0 +1,47 @@ +def solution(banana_list): + fewest_possible_number = len(banana_list) + for pairs in generate_trainer_pairs(list(range(len(banana_list)))): + possible_number = len(banana_list) + for i, j in pairs: + if check_loop(banana_list[i], banana_list[j]): + possible_number -= 2 + if possible_number < fewest_possible_number: + fewest_possible_number = possible_number + return fewest_possible_number + +import copy +def generate_trainer_pairs(trainer_id_list): + if len(trainer_id_list) < 2: + yield [] + else: + first_id = trainer_id_list[0] + temp_list = copy.deepcopy(trainer_id_list) + temp_list.remove(first_id) + for second_id in temp_list: + tt_list = copy.deepcopy(temp_list) + tt_list.remove(second_id) + for pairs in generate_trainer_pairs(tt_list): + yield [(first_id, second_id)] + pairs + +def check_loop(x, y): + a, b = x, y + memo = [] + while a != b: + if a > b: + a, b = a - b, 2*b + else: + a, b = 2*a, b - a + if (a, b) in memo: + return True + else: + memo += [(a, b)] + return False + +tests = [ + ([1, 1], 2), + ([1, 7, 3, 21, 13, 19], 0), + ] + +for i, o in tests: + result = solution(i) + print(i, result == o, result, o) -- 2.47.2 From cf6d493914bc6fbb89eed2741dfc3834836f78ce Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Sun, 24 Apr 2022 05:12:48 +0900 Subject: [PATCH 3/6] check sum is odd Verifying solution... Test 1 passed! Test 2 passed! Test 3 failed [Hidden] Test 4 failed [Hidden] Test 5 failed [Hidden] --- extra/distract-the-trainers/solution.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extra/distract-the-trainers/solution.py b/extra/distract-the-trainers/solution.py index a3f3918..0d12d81 100644 --- a/extra/distract-the-trainers/solution.py +++ b/extra/distract-the-trainers/solution.py @@ -24,6 +24,8 @@ def generate_trainer_pairs(trainer_id_list): yield [(first_id, second_id)] + pairs def check_loop(x, y): + if (x + y) % 2 == 1: # sum is odd + return True a, b = x, y memo = [] while a != b: @@ -38,6 +40,7 @@ def check_loop(x, y): return False tests = [ + ([1], 1), ([1, 1], 2), ([1, 7, 3, 21, 13, 19], 0), ] -- 2.47.2 From 6269b9b896eefe51acfa860e87a111f40430cdeb Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Sun, 24 Apr 2022 05:37:10 +0900 Subject: [PATCH 4/6] memoization and early termination Verifying solution... Test 1 passed! Test 2 passed! Test 3 failed [Hidden] Test 4 passed! [Hidden] Test 5 failed [Hidden] --- extra/distract-the-trainers/solution.py | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/extra/distract-the-trainers/solution.py b/extra/distract-the-trainers/solution.py index 0d12d81..356cf68 100644 --- a/extra/distract-the-trainers/solution.py +++ b/extra/distract-the-trainers/solution.py @@ -1,12 +1,15 @@ def solution(banana_list): fewest_possible_number = len(banana_list) + memo = {} for pairs in generate_trainer_pairs(list(range(len(banana_list)))): possible_number = len(banana_list) for i, j in pairs: - if check_loop(banana_list[i], banana_list[j]): + if check_loop(banana_list[i], banana_list[j], memo): possible_number -= 2 if possible_number < fewest_possible_number: fewest_possible_number = possible_number + if fewest_possible_number < 2: # early termination + break return fewest_possible_number import copy @@ -15,28 +18,25 @@ def generate_trainer_pairs(trainer_id_list): yield [] else: first_id = trainer_id_list[0] - temp_list = copy.deepcopy(trainer_id_list) - temp_list.remove(first_id) - for second_id in temp_list: - tt_list = copy.deepcopy(temp_list) - tt_list.remove(second_id) - for pairs in generate_trainer_pairs(tt_list): + for second_id in trainer_id_list[1:]: + reduced_list = copy.deepcopy(trainer_id_list) + seduced_list.remove(first_id) + reduced_list.remove(second_id) + for pairs in generate_trainer_pairs(reduced_list): yield [(first_id, second_id)] + pairs -def check_loop(x, y): - if (x + y) % 2 == 1: # sum is odd - return True - a, b = x, y - memo = [] +def check_loop(a, b, memo): + if (a, b) in memo: + return memo[(a, b)] while a != b: - if a > b: - a, b = a - b, 2*b - else: - a, b = 2*a, b - a - if (a, b) in memo: + if (a + b) % 2 == 1: # sum is odd + memo[(a, b)] = True return True + if a > b: + a, b = (a - b)/2, b else: - memo += [(a, b)] + a, b = a, (b - a)/2 + memo[(a, b)] = False return False tests = [ -- 2.47.2 From b7c9aa522697a57dc6c888a4a2fd145c105950ef Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Tue, 26 Apr 2022 02:55:09 +0900 Subject: [PATCH 5/6] finish extra/distract-the-trainers * Completed in: 1 day, 23 hrs, 21 secs. * Check Loop Proof 1. (ac, bc) => (2ac, bc-ac) = (a, b) => (2a, b-a) * a < b 1. (ac, bc+d) => (2ac, bc+d-ac) = (2ac, (b-a)c+d) * a < b and 0 < d < c 1. (a, 2n-a) => (2a, 2n-2a) = (a, n-a) * a < n 1. (x, y) will loop iff (x + y) % 2 == 1 * x and y are relative prime number * Reference * [Maximum Matching in General Graph](https://www.secmem.org/blog/2020/04/18/Blossom/) --- extra/distract-the-trainers/solution.py | 102 +++++++++++++++--------- 1 file changed, 66 insertions(+), 36 deletions(-) diff --git a/extra/distract-the-trainers/solution.py b/extra/distract-the-trainers/solution.py index 356cf68..4bcd847 100644 --- a/extra/distract-the-trainers/solution.py +++ b/extra/distract-the-trainers/solution.py @@ -1,48 +1,78 @@ def solution(banana_list): - fewest_possible_number = len(banana_list) - memo = {} - for pairs in generate_trainer_pairs(list(range(len(banana_list)))): - possible_number = len(banana_list) - for i, j in pairs: - if check_loop(banana_list[i], banana_list[j], memo): - possible_number -= 2 - if possible_number < fewest_possible_number: - fewest_possible_number = possible_number - if fewest_possible_number < 2: # early termination - break - return fewest_possible_number + loop_memo = {} + edge_map = {i: [] for i in range(len(banana_list))} + + for i in range(len(banana_list)): + for j in range(i+1, len(banana_list)): + if check_loop(banana_list[i], banana_list[j], loop_memo): + edge_map[i] += [j] + edge_map[j] += [i] + + return len(banana_list) - len(find_maximum_matching(edge_map)) + +def find_maximum_matching(edge_map): + exposed_vertex_list = [i for i in edge_map] + matching = {} + while len(exposed_vertex_list) >= 2: + start = exposed_vertex_list.pop() + path, end = find_new_path(edge_map, start, exposed_vertex_list, matching, {}) + if end in exposed_vertex_list: + update_matching(matching, start, path, end) + exposed_vertex_list.remove(end) + return matching import copy -def generate_trainer_pairs(trainer_id_list): - if len(trainer_id_list) < 2: - yield [] - else: - first_id = trainer_id_list[0] - for second_id in trainer_id_list[1:]: - reduced_list = copy.deepcopy(trainer_id_list) - seduced_list.remove(first_id) - reduced_list.remove(second_id) - for pairs in generate_trainer_pairs(reduced_list): - yield [(first_id, second_id)] + pairs +def find_new_path(edge_map, curr, end_list, matching, color): + for neighbor in edge_map[curr]: + if neighbor in end_list: + return [], neighbor + color[curr] = True + for neighbor in edge_map[curr]: + if neighbor in color: + continue + temp_color = copy.deepcopy(color) + temp_color[neighbor] = True + new_path, end = find_new_path(edge_map, matching[neighbor], end_list, matching, temp_color) + return [(curr, neighbor)] + new_path, end + return [], None + +def update_matching(matching, start, path, end): + new_left = start + for left, right in path: + matching[new_left] = left + matching[left] = new_left + new_left = right + matching[new_left] = end + matching[end] = new_left + +# How check_loop works +# 1. (ac, bc) => (2ac, bc-ac) = (a, b) => (2a, b-a) +# * a < b +# 1. (ac, bc+d) => (2ac, bc+d-ac) = (2ac, (b-a)c+d) +# * a < b and 0 < d < c +# 1. (a, 2n-a) => (2a, 2n-2a) = (a, n-a) +# * a < n +# 1. therefore, (x, y) will loop iff (x + y) % 2 == 1 +# * x and y are relative prime number +def check_loop(x, y, memo): + if (x, y) in memo: + return memo[(x, y)] + return bin((x + y)/gcd(x, y)).count('1') != 1 + +def gcd(m, n): + while n != 0: + t = m % n + m, n = n, t + return m -def check_loop(a, b, memo): - if (a, b) in memo: - return memo[(a, b)] - while a != b: - if (a + b) % 2 == 1: # sum is odd - memo[(a, b)] = True - return True - if a > b: - a, b = (a - b)/2, b - else: - a, b = a, (b - a)/2 - memo[(a, b)] = False - return False tests = [ ([1], 1), ([1, 1], 2), ([1, 7, 3, 21, 13, 19], 0), + #([1 for i in range(100)], 100), + #([2**(i+1) - 1 for i in range(10)], 2), + #([2**(i+1) - 1 for i in range(20)], 2), ] for i, o in tests: -- 2.47.2 From 24df09dd241fd1e077e3b64131477da1b48e8bc3 Mon Sep 17 00:00:00 2001 From: Seongbeom Park Date: Tue, 26 Apr 2022 02:57:56 +0900 Subject: [PATCH 6/6] update README.md --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 3330d88..dab81e8 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,16 @@ CFcSBwgCCBoHRhkKU1cGAA4AGU5YQR5THBwNFwoGGAxTQQMQVBUSBg4EAAwQRhUQVBUHFAQTGRpT QQM ### free-the-bunny-workers * Completed in: 3 hrs, 6 mins, 8 secs. +### distract-the-trainers +* Completed in: 1 day, 23 hrs, 21 secs. +* Check Loop Proof + 1. (ac, bc) => (2ac, bc-ac) = (a, b) => (2a, b-a) + * a < b + 1. (ac, bc+d) => (2ac, bc+d-ac) = (2ac, (b-a)c+d) + * a < b and 0 < d < c + 1. (a, 2n-a) => (2a, 2n-2a) = (a, n-a) + * a < n + 1. (x, y) will loop iff (x + y) % 2 == 1 + * x and y are relative prime number +* Reference + * [Maximum Matching in General Graph](https://www.secmem.org/blog/2020/04/18/Blossom/) -- 2.47.2