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/)
This commit is contained in:
parent
6269b9b896
commit
b7c9aa5226
@ -1,48 +1,78 @@
|
|||||||
def solution(banana_list):
|
def solution(banana_list):
|
||||||
fewest_possible_number = len(banana_list)
|
loop_memo = {}
|
||||||
memo = {}
|
edge_map = {i: [] for i in range(len(banana_list))}
|
||||||
for pairs in generate_trainer_pairs(list(range(len(banana_list)))):
|
|
||||||
possible_number = len(banana_list)
|
for i in range(len(banana_list)):
|
||||||
for i, j in pairs:
|
for j in range(i+1, len(banana_list)):
|
||||||
if check_loop(banana_list[i], banana_list[j], memo):
|
if check_loop(banana_list[i], banana_list[j], loop_memo):
|
||||||
possible_number -= 2
|
edge_map[i] += [j]
|
||||||
if possible_number < fewest_possible_number:
|
edge_map[j] += [i]
|
||||||
fewest_possible_number = possible_number
|
|
||||||
if fewest_possible_number < 2: # early termination
|
return len(banana_list) - len(find_maximum_matching(edge_map))
|
||||||
break
|
|
||||||
return fewest_possible_number
|
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
|
import copy
|
||||||
def generate_trainer_pairs(trainer_id_list):
|
def find_new_path(edge_map, curr, end_list, matching, color):
|
||||||
if len(trainer_id_list) < 2:
|
for neighbor in edge_map[curr]:
|
||||||
yield []
|
if neighbor in end_list:
|
||||||
else:
|
return [], neighbor
|
||||||
first_id = trainer_id_list[0]
|
color[curr] = True
|
||||||
for second_id in trainer_id_list[1:]:
|
for neighbor in edge_map[curr]:
|
||||||
reduced_list = copy.deepcopy(trainer_id_list)
|
if neighbor in color:
|
||||||
seduced_list.remove(first_id)
|
continue
|
||||||
reduced_list.remove(second_id)
|
temp_color = copy.deepcopy(color)
|
||||||
for pairs in generate_trainer_pairs(reduced_list):
|
temp_color[neighbor] = True
|
||||||
yield [(first_id, second_id)] + pairs
|
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 = [
|
tests = [
|
||||||
([1], 1),
|
([1], 1),
|
||||||
([1, 1], 2),
|
([1, 1], 2),
|
||||||
([1, 7, 3, 21, 13, 19], 0),
|
([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:
|
for i, o in tests:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user