├── 2004 └── bio2004q3.py ├── 2006 ├── bio2006q1.py └── bio2006q3.py ├── 2007 └── bio2007q3.py ├── 2008 └── bio2008.py ├── 2009 ├── bio2009q1.py ├── bio2009q2.py └── bio2009q3.py ├── 2010 ├── bio10q1.py └── bio10q2.py ├── 2011 ├── bio2011q1.py ├── bio2011q2.py └── bio2011q3.py ├── 2012 ├── bio2012q1.py └── bio2012q3.py ├── 2013 └── bio2013q3.py ├── 2014 ├── bio14q1.py ├── bio14q2.py └── bio2014q3.py ├── 2015 ├── bio15q1.py ├── bio15q2.py └── bio2015q3.py ├── 2016 ├── bio16q1.py ├── bio16q2.py └── bio16q3v2.py ├── 2017 ├── bio17q1.py ├── bio17q2.py └── bio17q3v2.py ├── 2018 ├── bio2018q1.py ├── bio2018q2.py └── bio2018q3ownImproved.py ├── 2019 ├── bio19q1.py ├── bio19q2.py └── bio19q3.py ├── 2020 ├── bio20q1.py ├── bio20q2.py └── bio20q3.py ├── 2021 ├── BIO2021q1.py ├── BIO2021q2.py └── BIO2021q3.py ├── .idea ├── .gitignore ├── British Informatics Olympiad.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── README.md └── docs ├── googled40a215ed9e79ad6.html └── index.html /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/British Informatics Olympiad.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /2004/bio2004q3.py: -------------------------------------------------------------------------------- 1 | morse_code = {"a": ".-", "h": "....", "o": "---", "v": "...-", "b": "-...", "i": "..", "p": ".--.", 2 | "w": ".--", "c": "-.-.", "j": ".---", "q": "--.-", "x": "-..-", "d": "-..", "k": "-.-", 3 | "r": ".-.", "y": "-.--", "e": ".", "l": ".-..", "s": "...", "z": "--..", "f": "..-.", 4 | "m": "--", "t": "-", "g": "--.", "n": "-.", "u": "..-"} 5 | ans = 0 6 | 7 | 8 | def find_words(morse, extra_letters, original_length): 9 | global ans 10 | if original_length == 0: 11 | return 12 | for letter in morse_code: 13 | c = len(morse_code[letter]) 14 | if c > extra_letters: 15 | continue 16 | if morse_code[letter] == morse[:c]: 17 | if c == extra_letters: 18 | if original_length == 1: 19 | ans += 1 20 | else: 21 | find_words(morse[c:], extra_letters-c, original_length-1) 22 | 23 | 24 | NAME = "" 25 | SCHOOL = "" 26 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 27 | word = input() 28 | m = "".join(morse_code[letter] for letter in word) 29 | find_words(m, len(m), len(word)) 30 | 31 | print(ans) 32 | -------------------------------------------------------------------------------- /2006/bio2006q1.py: -------------------------------------------------------------------------------- 1 | from string import ascii_uppercase 2 | 3 | def occurences(word): 4 | return tuple(word.count(letter) for letter in ascii_uppercase) 5 | 6 | NAME = "" 7 | SCHOOL = "" 8 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 9 | w1 = input() 10 | w2 = input() 11 | 12 | if occurences(w1) == occurences(w2): 13 | print("Anagrams") 14 | else: 15 | print("Not anagrams") -------------------------------------------------------------------------------- /2006/bio2006q3.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | NAME = "" 3 | SCHOOL = "" 4 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 5 | s, d = (int(x) for x in input().split()) 6 | 7 | @lru_cache(maxsize=None) 8 | def make_num(target, throws_left, throws_so_far=0, points=0): 9 | if points == target: 10 | if throws_left == 0: 11 | return 1 12 | elif points > target: 13 | return 0 14 | elif throws_left == 0: 15 | return 0 16 | 17 | total = 0 18 | if throws_so_far == 0: 19 | for i in range(1,21): 20 | total += make_num(target, throws_left - 1, throws_so_far + 1, points + i*2) 21 | else: 22 | for i in range(1,21): 23 | total += make_num(target, throws_left - 1, throws_so_far + 1, points + i) 24 | return total 25 | 26 | print(make_num(s, d)) -------------------------------------------------------------------------------- /2007/bio2007q3.py: -------------------------------------------------------------------------------- 1 | string = input() 2 | s, p = (int(x) for x in input().split()) 3 | 4 | def count(letter, steps): 5 | if letter == "A": 6 | a, b = 1, 0 7 | for i in range(steps): 8 | a, b = b, a + b 9 | return a + b 10 | 11 | if letter == "B": 12 | a, b = 0, 1 13 | for i in range(steps): 14 | a, b = b, a + b 15 | return a + b 16 | 17 | if letter == "C": 18 | return 2 ** steps 19 | 20 | if letter == "D": 21 | return 2 ** steps 22 | 23 | if letter == "E": 24 | return 2 ** steps 25 | 26 | ans = "" 27 | 28 | def change(word): 29 | new = "" 30 | for letter in word: 31 | if letter == "A": 32 | new += "B" 33 | elif letter == "B": 34 | new += "AB" 35 | elif letter == "C": 36 | new += "CD" 37 | elif letter == "D": 38 | new += "DC" 39 | elif letter == "E": 40 | new += "EE" 41 | return new 42 | 43 | for j in range(s, 0, -1): 44 | t = p 45 | for i in range(len(string)): 46 | x = count(string[i], j) 47 | if x >= t: 48 | string = change(string[:i+1]) 49 | break 50 | else: 51 | t -= x 52 | temp = string[:p] 53 | ans = f"{temp.count('A')} {temp.count('B')} {temp.count('C')} {temp.count('D')} {temp.count('E')}" 54 | print(ans) -------------------------------------------------------------------------------- /2008/bio2008.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | def move1(_string): 4 | temp = _string[1:4] 5 | _string = temp + _string[0] + _string[4:] 6 | return _string 7 | 8 | def move2(_string): 9 | temp = _string[3:6] 10 | _string = _string[:3] + _string[6] + temp 11 | return _string 12 | 13 | def move3(_string): 14 | temp = _string[:3] 15 | _string = _string[3] + temp + _string[4:] 16 | return _string 17 | 18 | def move4(_string): 19 | temp = _string[4:] 20 | _string = _string[:3] + temp + _string[3] 21 | return _string 22 | 23 | def bfs(): 24 | wanted = "1234567" 25 | seen = set() 26 | queue = deque() 27 | n = input() 28 | if n == wanted: return 0 29 | queue.append([n, 0]) 30 | seen.add(n) 31 | while queue: 32 | cur, moves_taken = queue.popleft() 33 | new = move1(cur) 34 | if new not in seen: 35 | if new == wanted: 36 | return moves_taken + 1 37 | queue.append([new, moves_taken + 1]) 38 | seen.add(new) 39 | 40 | new = move2(cur) 41 | if new not in seen: 42 | if new == wanted: 43 | return moves_taken + 1 44 | queue.append([new, moves_taken + 1]) 45 | seen.add(new) 46 | 47 | new = move3(cur) 48 | if new not in seen: 49 | if new == wanted: 50 | return moves_taken + 1 51 | queue.append([new, moves_taken + 1]) 52 | seen.add(new) 53 | 54 | new = move4(cur) 55 | if new not in seen: 56 | if new == wanted: 57 | return moves_taken + 1 58 | queue.append([new, moves_taken + 1]) 59 | seen.add(new) 60 | 61 | 62 | NAME = "" 63 | SCHOOL = "" 64 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 65 | print(bfs()) -------------------------------------------------------------------------------- /2009/bio2009q1.py: -------------------------------------------------------------------------------- 1 | def solve(word): 2 | n = len(word) 3 | possible = ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"] 4 | for digit in possible: 5 | pos = 0 6 | c = len(digit) 7 | for i in range(n): 8 | if word[i]==digit[pos]: 9 | pos += 1 10 | if pos==c: 11 | print(possible.index(digit) + 1) 12 | exit(0) 13 | print("NO") 14 | 15 | if __name__=="__main__": 16 | NAME = "" 17 | SCHOOL = "" 18 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 19 | word = input() 20 | solve(word) 21 | -------------------------------------------------------------------------------- /2009/bio2009q2.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | blocks = [[],[],[],[]] # Each sublist is a column 4 | for _ in range(4): 5 | row = input() 6 | for i in range(4): 7 | blocks[i].append(row[i]) 8 | 9 | original = [i.copy() for i in blocks] 10 | 11 | pos = [3, 3, 3, 3] 12 | 13 | def remove_blocks(id_list): # [x1, y1], ...] 14 | id_list.sort(key=lambda p: p[1], reverse=True) 15 | for id in id_list: 16 | if len(blocks[id[0]])==1: 17 | blocks[id[0]] = [] 18 | else: 19 | del blocks[id[0]][id[1]] 20 | 21 | def fill_column(column_id): 22 | blocks[column_id].reverse() 23 | for i in range(len(blocks[column_id]), 4): 24 | blocks[column_id].append(original[column_id][pos[column_id]]) 25 | pos[column_id] -= 1 26 | if pos[column_id] < 0: 27 | pos[column_id]=3 28 | blocks[column_id].reverse() 29 | 30 | def bfs(): 31 | global ans 32 | total = 1 33 | seen = set() 34 | points = 0 35 | to_be_deleted = [] 36 | for i in range(4): 37 | for j in range(4): 38 | if (i,j) not in seen: 39 | cur = blocks[i][j] 40 | queue = deque() 41 | queue.append((i,j)) 42 | points = 0 43 | temp = [(i,j)] 44 | while queue: 45 | xy = queue.popleft() 46 | points += 1 47 | seen.add(xy) 48 | if xy[0] < 3: 49 | if (xy[0]+1, xy[1]) not in seen: 50 | if blocks[xy[0]+1][xy[1]] == cur: 51 | queue.append((xy[0]+1, xy[1])) 52 | temp.append((xy[0]+1, xy[1])) 53 | seen.add((xy[0]+1, xy[1])) 54 | if xy[1] < 3: 55 | if (xy[0], xy[1]+1) not in seen: 56 | if blocks[xy[0]][xy[1]+1] == cur: 57 | queue.append((xy[0], xy[1]+1)) 58 | temp.append((xy[0], xy[1]+1)) 59 | seen.add((xy[0], xy[1]+1)) 60 | 61 | if xy[0]: 62 | if (xy[0]-1, xy[1]) not in seen: 63 | if blocks[xy[0] - 1][xy[1]] == cur: 64 | queue.append((xy[0]-1, xy[1])) 65 | temp.append((xy[0]-1, xy[1])) 66 | seen.add((xy[0]-1, xy[1])) 67 | if xy[1]: 68 | if (xy[0], xy[1]-1) not in seen: 69 | if blocks[xy[0]][xy[1]-1] == cur: 70 | queue.append((xy[0], xy[1]-1)) 71 | temp.append((xy[0], xy[1]-1)) 72 | seen.add((xy[0], xy[1]-1)) 73 | 74 | if points > 1: 75 | total *= points 76 | to_be_deleted.extend(temp) 77 | if not to_be_deleted: 78 | print(ans) 79 | print("GAME OVER") 80 | exit(0) 81 | remove_blocks(to_be_deleted) 82 | for i in range(4): 83 | fill_column(i) 84 | return total if total > 1 else 0 85 | 86 | 87 | NAME = "" 88 | SCHOOL = "" 89 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 90 | ans = 0 91 | 92 | while True: 93 | N = int(input()) 94 | if N == 0: 95 | exit(0) 96 | for _ in range(N): 97 | ans += bfs() 98 | print("\n".join("".join(blocks[j][i] for j in range(4)) for i in range(4))) 99 | print(ans) -------------------------------------------------------------------------------- /2009/bio2009q3.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | @lru_cache(maxsize=None) 4 | def num_arrangements(n): 5 | if n > 9: 6 | t = 0 7 | for i in range(n-9,n): 8 | t += num_arrangements(i) 9 | return t 10 | else: 11 | return 2**(n-1) if n else 1 12 | 13 | answers = [] 14 | def solve(): 15 | n, query = (int(x) for x in input().split()) 16 | 17 | ans = [] 18 | while True: 19 | if n == 0: 20 | break 21 | for i in range(1, min(10, n+1)): 22 | c = num_arrangements(n-i) 23 | if c >= query: # Right prefix 24 | ans.append(str(i)) 25 | n -= i 26 | break 27 | else: 28 | query -= c 29 | print(" ".join(ans)) 30 | 31 | NAME = "" 32 | SCHOOL = "" 33 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 34 | solve() -------------------------------------------------------------------------------- /2010/bio10q1.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | NAME = "" 3 | SCHOOL = "" 4 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 5 | n=int(input()) 6 | anagram = defaultdict(int) 7 | k = n 8 | while k!=0: 9 | anagram[k%10] += 1 10 | k//=10 11 | ans = [] 12 | for i in range(2,10): 13 | original = anagram.copy() 14 | new = n*i 15 | ok = True 16 | while new!=0: 17 | if not original[new%10]: 18 | ok = False 19 | break 20 | original[new%10] -= 1 21 | new//=10 22 | if ok: 23 | ans.append(str(i)) 24 | print(" ".join(ans) if ans else "NO") 25 | """ 26 | c = 85247910 27 | anagram2 = defaultdict(int) 28 | while c!=0: 29 | anagram2[c%10] += 1 30 | c//=10 31 | c = 85247910 32 | for i in range(2,10): 33 | if not c%i: 34 | original = anagram2.copy() 35 | new = c//i 36 | if len(str(new)) != len(str(c)): 37 | continue 38 | ok = True 39 | while new!=0: 40 | if not original[new%10]: 41 | ok = False 42 | break 43 | original[new%10] -= 1 44 | new//=10 45 | if ok: 46 | print(c//i) 47 | """ -------------------------------------------------------------------------------- /2010/bio10q2.py: -------------------------------------------------------------------------------- 1 | class Grid(): 2 | def __init__(self, row1, row2, row3): 3 | self.grid = [[1 for _ in range(11)] for _ in range(11)] 4 | self.grid[4][4:7]=row1 5 | self.grid[5][4:7]=row2 6 | self.grid[6][4:7]=row3 7 | 8 | def change_value(self, number, y, x): 9 | self.grid[y][x]+=number 10 | self.grid[y][x]-=1 11 | self.grid[y][x]%=6 12 | self.grid[y][x]+=1 13 | 14 | 15 | class Dice(): 16 | def __init__(self, grid): # Default values 17 | self.faces = {1: "top", 6: "bottom", 3: "left", 4: "right", 5: "front", 2: "back"} 18 | self.values = {"top": 1, "bottom": 6, "left": 3, "right": 4, "front": 5, "back": 2} 19 | self.pos = (5,5) 20 | self.grid = grid 21 | self.heading = "back" 22 | self.directions = {"front": (1,0), "right": (0,1), "back": (-1,0), "left": (0,-1)} 23 | 24 | def change_face(self, direction1, direction2): 25 | v1 = self.values[direction1] 26 | self.faces[v1]=direction2 27 | 28 | def update_values(self): 29 | for i in self.faces: 30 | self.values[self.faces[i]]=i 31 | 32 | def rotate(self, direction): 33 | if direction=="left": 34 | # Left becomes bottom, right becomes top, bottom becomes right, top becomes left 35 | self.change_face("left", "bottom") 36 | self.change_face("right", "top") 37 | self.change_face("bottom", "right") 38 | self.change_face("top", "left") 39 | elif direction=="right": 40 | self.change_face("right", "bottom") 41 | self.change_face("left", "top") 42 | self.change_face("bottom", "left") 43 | self.change_face("top", "right") 44 | elif direction=="front": 45 | self.change_face("front", "bottom") 46 | self.change_face("back", "top") 47 | self.change_face("bottom", "back") 48 | self.change_face("top", "front") 49 | elif direction=="back": 50 | self.change_face("back", "bottom") 51 | self.change_face("front", "top") 52 | self.change_face("bottom", "front") 53 | self.change_face("top", "back") 54 | 55 | self.update_values() 56 | 57 | def move(self): 58 | y, x = self.pos 59 | self.grid.change_value(self.values["top"], y, x) 60 | value = self.grid.grid[y][x] 61 | if value == 1 or value == 6: pass 62 | elif value == 3 or value == 4: 63 | self.heading = self.faces[7 - self.values[self.heading]] 64 | elif value == 2: 65 | if self.heading == "front": 66 | self.heading = "left" 67 | elif self.heading == "back": 68 | self.heading = "right" 69 | elif self.heading == "left": 70 | self.heading = "back" 71 | elif self.heading == "right": 72 | self.heading = "front" 73 | elif value == 5: 74 | if self.heading == "left": 75 | self.heading = "front" 76 | elif self.heading == "right": 77 | self.heading = "back" 78 | elif self.heading == "back": 79 | self.heading = "left" 80 | elif self.heading == "front": 81 | self.heading = "right" 82 | 83 | a, b = self.directions[self.heading] 84 | self.pos = ((self.pos[0] + a)%11, (self.pos[1] + b)%11) 85 | self.rotate(self.heading) 86 | 87 | def __str__(self): 88 | y = self.pos[0] 89 | x = self.pos[1] 90 | ans = [] 91 | for j in range(y-1,y+2): 92 | temp = "" 93 | for i in range(x-1,x+2): 94 | if j>10 or i>10 or j<0 or i<0: 95 | temp += "X" 96 | else: 97 | temp += str(self.grid.grid[j][i]) 98 | ans.append(temp) 99 | return "\n".join(ans) 100 | 101 | 102 | NAME = "" 103 | SCHOOL = "" 104 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 105 | 106 | rows = [[int(x) for x in input().split()] for _ in range(3)] 107 | grid = Grid(rows[0], rows[1], rows[2]) 108 | dice = Dice(grid) 109 | 110 | while True: 111 | n = int(input()) 112 | if n==0: 113 | break 114 | for _ in range(n): 115 | dice.move() 116 | print(dice) -------------------------------------------------------------------------------- /2011/bio2011q1.py: -------------------------------------------------------------------------------- 1 | NAME = "" 2 | SCHOOL = "" 3 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 4 | 5 | a, b, n = input().split() 6 | n = int(n) 7 | 8 | if n == 1: 9 | print(a) 10 | exit(0) 11 | if n == 2: 12 | print(b) 13 | exit(0) 14 | 15 | t = ord("A")-1 16 | x, y = ord(a)-t, ord(b)-t 17 | 18 | seen = {} 19 | for i in range(2,n): 20 | x, y = y, x+y 21 | if y>26: 22 | y -= 26 23 | """ 24 | if (x,y) in seen: 25 | print(i) 26 | print(seen[(x,y)]) 27 | break 28 | else: 29 | seen[(x,y)]=i 30 | """ 31 | 32 | 33 | print(chr(y+t)) 34 | # print(chr(t+ord("X")-ord("F"))) 35 | ## R 36 | # print(chr(t+26+ord("H")-ord("Q"))) 37 | ## Q 38 | 39 | # print(10**15 % 84) 40 | ## 74 41 | # C C 75 = K -------------------------------------------------------------------------------- /2011/bio2011q2.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | NAME = "" 4 | SCHOOL = "" 5 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 6 | 7 | values = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K"] 8 | suits = ["C", "H", "S", "D"] 9 | before_dealing = deque() 10 | for s in suits: 11 | for i in values: 12 | before_dealing.append(i+s) 13 | 14 | piles = [] 15 | abcdef = [int(x) for x in input().split()] 16 | 17 | i = 0 18 | seen = set() 19 | while before_dealing: 20 | for _ in range(abcdef[i]-1): 21 | before_dealing.append(before_dealing.popleft()) 22 | piles.append([1, before_dealing.popleft()]) 23 | i += 1 24 | i %= 6 25 | 26 | print(f"{piles[0][1]} {piles[-1][1]}") 27 | 28 | original = [piles[i].copy() for i in range(len(piles))] 29 | # print(original) 30 | 31 | def move(index1, index2): # Move pile one onto pile 2 32 | piles[index2][0] += piles[index1][0] 33 | piles[index2][1] = piles[index1][1] 34 | del piles[index1] 35 | 36 | def check_validity(index1, index2): 37 | if index2 < 0: 38 | return False 39 | if piles[index1][1][0] == piles[index2][1][0] or piles[index1][1][1] == piles[index2][1][1]: 40 | return True 41 | return False 42 | 43 | game_continue = True 44 | while game_continue: 45 | if len(piles)==1: 46 | break 47 | i = len(piles)-1 48 | while True: 49 | if i == 0: # No valid moves 50 | game_continue=False 51 | break 52 | 53 | # Check adjacent pile 54 | if check_validity(i, i-1): 55 | move(i, i-1) 56 | break 57 | 58 | # Check separated pile 59 | elif check_validity(i, i-3): 60 | move(i,i-3) 61 | break 62 | 63 | i -= 1 64 | 65 | print(f"{len(piles)} {piles[0][1]}") 66 | 67 | # print(piles) 68 | piles = [original[i].copy() for i in range(len(original))] 69 | # print(piles) 70 | 71 | while True: 72 | if len(piles)==1: 73 | break 74 | largest = 0 75 | indices = [] 76 | for i in range(len(piles)-1, 0, -1): 77 | if check_validity(i, i-1): 78 | c = largest 79 | largest = max(largest, piles[i][0] + piles[i-1][0]) 80 | if c!=largest: 81 | indices = [i, i-1] 82 | if check_validity(i, i-3): 83 | c = largest 84 | largest = max(largest, piles[i][0] + piles[i-3][0]) 85 | if c!=largest: 86 | indices = [i, i-3] 87 | if not indices: 88 | break 89 | move(indices[0], indices[1]) 90 | 91 | print(f"{len(piles)} {piles[0][1]}") 92 | 93 | piles = [original[i].copy() for i in range(len(original))] 94 | # piles = [[1, "AD"], [3, "8S"], [1, "8D"], [2, "4S"], [1, "TH"], [2, "KD"], [2, "4D"], [1, "TC"]] 95 | 96 | def find_valid(index1, index2): 97 | temp = [piles[i].copy() for i in range(len(piles))] 98 | temp[index2][0] += temp[index1][0] 99 | temp[index2][1] = temp[index1][1] 100 | del temp[index1] 101 | 102 | moves = 0 103 | 104 | def valid(i1, i2): 105 | if i2 < 0: 106 | return False 107 | if temp[i1][1][0] == temp[i2][1][0] or temp[i1][1][1] == temp[i2][1][1]: 108 | return True 109 | return False 110 | 111 | for i in range(len(temp)-1, 0, -1): 112 | if valid(i, i-1): 113 | moves += 1 114 | if valid(i, i-3): 115 | moves += 1 116 | 117 | return moves 118 | 119 | while True: 120 | if len(piles)==1: 121 | break 122 | largest = 0 123 | indices = [] 124 | possible = False 125 | for i in range(len(piles)-1, 0, -1): 126 | if check_validity(i, i-1): 127 | possible = True 128 | c = largest 129 | largest = max(largest, find_valid(i, i-1)) 130 | if c!=largest or largest==0: 131 | indices = [i, i-1] 132 | if check_validity(i, i-3): 133 | possible = True 134 | c = largest 135 | largest = max(largest, find_valid(i, i-3)) 136 | if c!=largest or largest==0: 137 | indices = [i, i-3] 138 | if not indices and not possible: 139 | break 140 | move(indices[0], indices[1]) 141 | 142 | print(f"{len(piles)} {piles[0][1]}") -------------------------------------------------------------------------------- /2011/bio2011q3.py: -------------------------------------------------------------------------------- 1 | from bisect import bisect_left 2 | 3 | NAME = "" 4 | SCHOOL = "" 5 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 6 | n = int(input()) 7 | import time 8 | start = time.time() 9 | 10 | upside_down_numbers = [5, 19, 28, 37, 46, 55, 64, 73, 82, 91] 11 | if n<=10: 12 | print(upside_down_numbers[n-1]) 13 | exit(0) 14 | 15 | n -= 10 16 | min = 10 17 | while True: 18 | new = [] 19 | pos = bisect_left(upside_down_numbers, min) 20 | min *= 10 21 | new = [] 22 | k = 9 * pos 23 | for j in range(1,10): 24 | for i in range(pos): 25 | new.append(j*min + upside_down_numbers[i]*10 + 10-j) 26 | n -= 1 27 | if n==0: 28 | print(new[-1]) 29 | print(time.time()-start) 30 | exit(0) 31 | upside_down_numbers = [upside_down_numbers[i] for i in range(pos, len(upside_down_numbers))] + new -------------------------------------------------------------------------------- /2012/bio2012q1.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | def solve(): 4 | n = int(input()) 5 | ans = 1 6 | for i in range(2, int(n**0.5)+1): 7 | if not n%i: 8 | while not n%i: 9 | n //= i 10 | ans *= i 11 | if not n: 12 | return ans 13 | if ans == 1: # Prime 14 | return n 15 | return ans 16 | 17 | def c(n): 18 | ans = 1 19 | for i in range(2, int(n ** 0.5) + 1): 20 | if not n % i: 21 | while not n % i: 22 | n //= i 23 | ans *= i 24 | if not n: 25 | return ans 26 | if ans == 1: # Prime 27 | return n 28 | return ans 29 | 30 | if __name__ == "__main__": 31 | NAME = "" 32 | SCHOOL = "" 33 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 34 | print(solve()) 35 | # print("10, 20, 40, 50, 80, 100, 160, 200, 250, 320") 36 | seen = defaultdict(int) 37 | for i in range(1,1000001): 38 | if not i%10000: 39 | print(i) 40 | seen[c(i)] += 1 41 | occurences = 0 42 | largest = 0 43 | for x in seen: 44 | if seen[x] > occurences: 45 | largest = x 46 | occurences = seen[x] 47 | print(largest) -------------------------------------------------------------------------------- /2012/bio2012q3.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | # import time 3 | 4 | digit_words = {1: "ONE", 2: "TWO", 3: "THREE", 4: "FOUR", 5: "FIVE", 6: "SIX", 5 | 7: "SEVEN", 8: "EIGHT", 9: "NINE", 0: "ZERO"} 6 | words = {"ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "ZERO"} 7 | letters = ["O", "N", "E", "T", "W", "H", "R", "F", "U", "I", "V", "S", "X", "G", "Z"] 8 | 9 | def number_to_word(n): 10 | if n==-1: 11 | return [""] 12 | t = [] 13 | while n: 14 | t.append(digit_words[n % 10]) 15 | n //= 10 16 | return t 17 | 18 | def solve(): 19 | answers = [] 20 | for _ in range(3): 21 | s, f = (int(x) for x in input().split()) 22 | # start = time.time() 23 | ts, tf = str(s), str(f) 24 | tts = ts 25 | for digit in tts: 26 | if digit in tf and digit in ts: 27 | tf = tf.replace(digit, "") 28 | ts = ts.replace(digit, "") 29 | s, f = int(ts) if ts else -1, int(tf) if tf else -1 30 | a, b = number_to_word(s), number_to_word(f) 31 | print(bfs("".join(a[::-1]), "".join(b[::-1]))) 32 | # print(time.time()-start) 33 | 34 | def count(start): # Returns tuple state 35 | return tuple((start.count("O"), start.count("N"), 36 | start.count("E"), start.count("T"), 37 | start.count("W"), start.count("H"), 38 | start.count("R"), start.count("F"), 39 | start.count("U"), start.count("I"), 40 | start.count("V"), start.count("S"), 41 | start.count("X"), start.count("G"), 42 | start.count("Z"))) 43 | 44 | numbers = [count("".join(number_to_word(i)[::-1])) for i in range(1,1000)] 45 | 46 | def find_all_states(state): 47 | states = [] 48 | for i in range(999): 49 | if sum(abs(numbers[i][j]-state[j]) for j in range(15)) < 6: 50 | states.append(numbers[i]) 51 | return states 52 | 53 | def bfs(start, target): 54 | # Numbers are made of O, N, E, T, W, H, R, F, U, I, V, S, X, G, Z 55 | start_state = count(start) 56 | end_state = count(target) 57 | seen = set() 58 | queue = deque() 59 | queue.append([start_state,0]) 60 | while queue: 61 | cur = queue.popleft() 62 | if cur[0] in seen: 63 | continue 64 | seen.add(cur[0]) 65 | new_states = find_all_states(cur[0]) 66 | for i in range(len(new_states)): 67 | if new_states[i]==end_state: 68 | return cur[1]+1 69 | else: 70 | if new_states[i] not in seen: 71 | queue.append([new_states[i], cur[1]+1]) 72 | # print(new_states[i]) 73 | 74 | 75 | if __name__ == "__main__": 76 | NAME = "" 77 | SCHOOL = "" 78 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 79 | solve() -------------------------------------------------------------------------------- /2013/bio2013q3.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | grid = [[0 for _ in range(5)] for _ in range(5)] 4 | 5 | def solve(): 6 | pos_list = convert_to_num(input()) 7 | for y,x, n in pos_list: 8 | grid[y][x] += n 9 | print(bfs(convert_to_string(grid))) 10 | 11 | 12 | def convert_to_num(string): 13 | lst = [] 14 | for letter in string: 15 | if letter.isupper(): 16 | ans = ord(letter) - ord("A") 17 | lst.append([ans // 5, ans % 5, 2]) 18 | else: 19 | ans = ord(letter) - ord("a") 20 | lst.append([ans // 5, ans % 5, 1]) 21 | return lst 22 | 23 | def convert_to_string(array): 24 | ans = "" 25 | for i in range(5): 26 | for j in range(5): 27 | if not array[i][j]: 28 | continue 29 | if array[i][j]==1: 30 | ans += chr(i*5+j + ord("a")) 31 | else: 32 | ans += chr(i*5+j + ord("A")) 33 | return ans 34 | 35 | def light(array, position, presses): 36 | y, x = position // 5, position % 5 37 | array[y][x] = (array[y][x] + presses) % 3 38 | if y: array[y-1][x] = (array[y-1][x] + presses) % 3 39 | if x: array[y][x-1] = (array[y][x-1] + presses) % 3 40 | if y != 4: array[y+1][x] = (array[y+1][x] + presses) % 3 41 | if x != 4: array[y][x+1] = (array[y][x+1] + presses) % 3 42 | 43 | def bfs(string): 44 | start = string 45 | prev = {string: ""} 46 | prev_action = {string: ""} 47 | queue = deque() 48 | queue.append(string) 49 | while queue: 50 | cur = queue.popleft() 51 | for i in range(25): 52 | for j in range(1,3): 53 | x = [[0 for _ in range(5)] for _ in range(5)] 54 | pos_list = convert_to_num(cur) 55 | for y, z, n in pos_list: 56 | x[y][z] += n 57 | light(x, i, j) 58 | new = convert_to_string(x) 59 | if new not in prev: 60 | if new == "": 61 | actions = chr(i + ord("a")) if j==1 else chr(i + ord("A")) 62 | while cur != start: 63 | pos, presses = prev_action[cur] 64 | if presses==1: 65 | actions += chr(pos + ord("a")) 66 | else: 67 | actions += chr(pos + ord("A")) 68 | cur = prev[cur] 69 | return actions[::-1] 70 | else: 71 | prev[new] = cur 72 | prev_action[new] = (i, j) 73 | queue.append(new) 74 | return "IMPOSSIBLE" 75 | 76 | if __name__ == "__main__": 77 | NAME = "" 78 | SCHOOL = "" 79 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 80 | solve() -------------------------------------------------------------------------------- /2014/bio14q1.py: -------------------------------------------------------------------------------- 1 | import time 2 | from bisect import bisect_left, bisect_right 3 | 4 | def lucky(lst, first): 5 | for j in range(len(lst)-1, -1, -1): 6 | if (j-1) and not (j+1)%first: 7 | del lst[j] 8 | return lst 9 | 10 | if __name__ == '__main__': 11 | NAME = "" 12 | SCHOOL = "" 13 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 14 | n = int(input()) 15 | start = time.time() 16 | odd = [2*i+1 for i in range(5200)] 17 | first_number = odd[1] 18 | index = 1 19 | while first_number < n and index<16: 20 | lucky(odd, first_number) 21 | index += 1 22 | first_number = odd[index] 23 | 24 | print(f"{odd[bisect_left(odd, n)-1]} {odd[bisect_right(odd, n)]}") 25 | 26 | print(time.time()-start) 27 | -------------------------------------------------------------------------------- /2014/bio14q2.py: -------------------------------------------------------------------------------- 1 | 2 | redTiles = {1: [[1,1,3,4,5,6],[(-1,0),(1,0),(1,0),(1,0),(-1,0),(-1,0)]], 2: [[2,2,3,4,5,6],[(0,-1),(0,1),(0,1),(0,-1),(0,-1),(0,1)]], 3 | 3: [[1,2,4,5,5,6],[(-1,0),(0,-1),(0,-1),(-1,0),(0,-1),(-1,0)]], 4: [[1,2,3,5,6,6],[(-1,0),(0,1),(0,1),(-1,0),(0,1),(-1,0)]], 4 | 5: [[1,2,3,3,4,6],[(1,0),(0,-1),(1,0),(0,1),(1,0),(0,1)]], 6: [[1,2,3,4,4,5],[(1,0),(0,-1),(1,0),(1,0),(0,-1),(0,-1)]]} 5 | 6 | greenTiles = {2: [[2,2,5,6,3,4],[(-1,0),(1,0),(1,0),(1,0),(-1,0),(-1,0)]], 1: [[1,1,5,6,3,4],[(0,-1),(0,1),(0,1),(0,-1),(0,-1),(0,1)]], 7 | 5: [[2,1,6,3,3,4],[(-1,0),(0,-1),(0,-1),(-1,0),(0,-1),(-1,0)]], 6: [[2,1,5,3,4,4],[(-1,0),(0,1),(0,1),(-1,0),(0,1),(-1,0)]], 8 | 3: [[2,1,5,5,6,4],[(1,0),(0,-1),(1,0),(0,1),(1,0),(0,1)]], 4: [[2,1,5,6,6,3],[(1,0),(0,-1),(1,0),(1,0),(0,-1),(0,-1)]]} 9 | 10 | r_total = 0 11 | g_total = 0 12 | found = False 13 | 14 | 15 | def RedConnect(pos, start, used, points=0): 16 | currentUsed = used.copy() 17 | global r_total, found 18 | if pos==start and points>3: 19 | r_total += points/sum(1 for i in used if grid[i[0]][i[1]]==5 ) 20 | found = True 21 | return 22 | row, column = pos[0], pos[1] 23 | directions = redTiles[grid[row][column]][1] 24 | wantedTiles = redTiles[grid[row][column]][0] 25 | connections = 0 26 | for k in range(len(directions)): 27 | up, right = directions[k] 28 | used = currentUsed.copy() 29 | try: 30 | if grid[row + up][column + right] == wantedTiles[k] and row +up >=0 and column + right >=0: 31 | connections += 1 32 | newTile = [row + up,column + right] 33 | if newTile not in used or (newTile==start and points > 2): 34 | if newTile not in used: 35 | used.append([row, column]) 36 | RedConnect(newTile, start,used,points + 1) 37 | if found: 38 | break 39 | except IndexError: 40 | pass 41 | 42 | def GreenConnect(pos, start, used, points=0): 43 | currentUsed = used.copy() 44 | global g_total, found 45 | if pos==start and points>3: 46 | g_total += points/sum(1 for i in used if grid[i[0]][i[1]]==3 ) 47 | found = True 48 | return 49 | row, column = pos[0], pos[1] 50 | directions = greenTiles[grid[row][column]][1] 51 | wantedTiles = greenTiles[grid[row][column]][0] 52 | connections = 0 53 | for k in range(len(directions)): 54 | up, right = directions[k] 55 | used = currentUsed.copy() 56 | try: 57 | if grid[row + up][column + right] == wantedTiles[k] and row +up >=0 and column + right >=0: 58 | connections += 1 59 | newTile = [row + up,column + right] 60 | if newTile not in used or (newTile==start and points > 2): 61 | if newTile not in used: 62 | used.append([row, column]) 63 | GreenConnect(newTile, start,used,points + 1) 64 | if found: 65 | break 66 | except IndexError: 67 | pass 68 | 69 | 70 | NAME = "" 71 | SCHOOL = "" 72 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 73 | n = int(input()) 74 | grid = [[int(x) for x in input().split()] for _ in range(n)] 75 | 76 | # Red Player 77 | for row in range(n-1): 78 | for column in range(n-1): 79 | if grid[row][column] != 5: 80 | continue 81 | found = False 82 | RedConnect([row, column], [row, column],[]) 83 | 84 | 85 | # Green Player 86 | for row in range(n-1): 87 | for column in range(n-1): 88 | if grid[row][column] != 3: 89 | continue 90 | found = False 91 | GreenConnect([row, column], [row, column],[]) 92 | 93 | print(f"{int(r_total)} {int(g_total)}") 94 | -------------------------------------------------------------------------------- /2014/bio2014q3.py: -------------------------------------------------------------------------------- 1 | import math 2 | from functools import lru_cache 3 | @lru_cache(maxsize=None) 4 | def comb(n, k): 5 | return math.factorial(n) // math.factorial(k) // math.factorial(n - k) 6 | 7 | 8 | NAME = "" 9 | SCHOOL = "" 10 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 11 | n = int(input()) 12 | 13 | alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 14 | 15 | 16 | def build(n, targetLen, ans=""): 17 | if targetLen == 0: 18 | return ans 19 | start = (alpha.index(ans[-1]) + 1) if ans else 0 20 | for letter in range(start, 36): 21 | 22 | can = comb(36 - (letter + 1), targetLen - 1) 23 | print(can) 24 | 25 | if can >= n: 26 | return build(n, targetLen - 1, ans + alpha[letter]) # Right prefix 27 | n -= can 28 | 29 | 30 | targLen = 1 31 | while comb(36, targLen) < n: 32 | n -= comb(36, targLen) 33 | targLen += 1 34 | print(n) 35 | 36 | print(build(n, targLen)) -------------------------------------------------------------------------------- /2015/bio15q1.py: -------------------------------------------------------------------------------- 1 | string = input() 2 | 3 | def block_palindrome(string): 4 | blocks = 0 5 | for i in range(1, len(string)//2+1): 6 | if (string[:i] == string[-i:]): 7 | blocks += 1 + block_palindrome(string[i:-i]) 8 | return blocks 9 | 10 | NAME = "" 11 | SCHOOL = "" 12 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 13 | print(block_palindrome(string)) 14 | -------------------------------------------------------------------------------- /2015/bio15q2.py: -------------------------------------------------------------------------------- 1 | NAME = "" 2 | SCHOOL = "" 3 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 4 | a, c, m = (int(x) for x in input().split()) 5 | r = 0 6 | 7 | class Grid(): 8 | def __init__(self): 9 | self.grid = [[0 for _ in range(10)] for __ in range(10)] 10 | 11 | def place(self, x,y): 12 | self.grid[9-y][x] = 1 13 | 14 | def valid(self, x,y): 15 | try: 16 | if self.grid[9-y][x]: 17 | return False 18 | # If that square is not on the board, it isn't valid 19 | except IndexError: 20 | return False 21 | try: 22 | if self.grid[9-y][x-1]: 23 | return False 24 | except IndexError: pass 25 | try: 26 | if self.grid[9-y][x+1]: 27 | return False 28 | except IndexError: pass 29 | try: 30 | if self.grid[8-y][x-1]: 31 | return False 32 | except IndexError: pass 33 | try: 34 | if self.grid[8-y][x+1]: 35 | return False 36 | except IndexError: pass 37 | try: 38 | if self.grid[8-y][x]: 39 | return False 40 | except IndexError: pass 41 | try: 42 | if self.grid[10-y][x]: 43 | return False 44 | except IndexError: pass 45 | try: 46 | if self.grid[10-y][x+1]: 47 | return False 48 | except IndexError: pass 49 | try: 50 | if self.grid[10-y][x-1]: 51 | return False 52 | except IndexError: pass 53 | 54 | return True 55 | 56 | toPlace = [4,3,3,2,2,2,1,1,1,1] 57 | board = Grid() 58 | 59 | for ship in toPlace: 60 | valid = False 61 | while True: 62 | r = (a*r+c)%m 63 | 64 | x = r%10 65 | y = (r%100)//10 66 | r = (a*r+c)%m 67 | d = r%2 68 | direction = "V" if d else "H" 69 | ty = y 70 | tx = x 71 | b = False 72 | for _ in range(ship): 73 | if board.valid(tx, ty): 74 | if d: 75 | ty += 1 76 | else: 77 | tx += 1 78 | else: 79 | b = True 80 | break 81 | if b: 82 | continue 83 | ty = y 84 | tx = x 85 | for _ in range(ship): 86 | board.place(tx,ty) 87 | if d: 88 | ty += 1 89 | else: 90 | tx += 1 91 | 92 | print(f"{x} {y} {direction}") 93 | break 94 | -------------------------------------------------------------------------------- /2015/bio2015q3.py: -------------------------------------------------------------------------------- 1 | # 30 / 32 2 | 3 | """ 4 | from functools import lru_cache 5 | 6 | @lru_cache(maxsize=None) 7 | def factorial(n): 8 | if n==0: 9 | return 1 10 | else: 11 | return n*factorial(n-1) 12 | 13 | a, b, c, d, n = (int(x) for x in input().split()) 14 | Sum = a+b+c+d 15 | temp = [a,b,c,d] 16 | 17 | total = factorial(Sum)/factorial(a)/factorial(b)/factorial(c)/factorial(d) 18 | 19 | ans = "" 20 | 21 | while any(temp): 22 | c = total/sum(temp) 23 | pos = n/c 24 | 25 | k = 0 26 | for i in range(len(temp)): 27 | if temp[i]==0: 28 | k += 1 29 | if pos>temp[i]: 30 | pos -= temp[i] 31 | else: 32 | break 33 | ans += "ABCD"[i] 34 | 35 | n -= c*sum(temp[:i]) 36 | temp[i] -= 1 37 | total = factorial(sum(temp)) 38 | for k in temp: 39 | total /= factorial(k) 40 | 41 | print(ans) 42 | """ 43 | 44 | from functools import lru_cache 45 | 46 | @lru_cache(maxsize=None) 47 | def factorial(n): 48 | if n==0: 49 | return 1 50 | else: 51 | return n*factorial(n-1) 52 | def total_ways(a, b, c, d): 53 | return factorial(a+b+c+d)//(factorial(a)*factorial(b)*factorial(c)*factorial(d)) 54 | 55 | def g(a, b, c, d, n): 56 | if a==0 and b==0 and c==0 and d==0: 57 | return '' 58 | ways=total_ways(a, b, c, d) 59 | 60 | if n <= (ways*a)//(a+b+c+d) and a>0: ##letter is 'A' 61 | return 'A'+g(a-1, b, c, d, n) 62 | 63 | if n <= (ways*(a+b))//(a+b+c+d) and b>0: ##letter is 'B' 64 | return 'B'+g(a, b-1, c, d, n-(ways*a)//(a+b+c+d)) 65 | 66 | if n <= (ways*(a+b+c))//(a+b+c+d) and c>0: ##letter is 'C' 67 | return 'C'+g(a, b, c-1, d, n-(ways*(a+b))//(a+b+c+d)) 68 | 69 | else: ##letter is 'D' 70 | return 'D'+g(a, b, c, d-1, n-(ways*(a+b+c))//(a+b+c+d)) 71 | 72 | NAME = "" 73 | SCHOOL = "" 74 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 75 | a, b, c, d, n = (int(x) for x in input().split()) 76 | 77 | print(g(a, b, c, d, n)) 78 | -------------------------------------------------------------------------------- /2016/bio16q1.py: -------------------------------------------------------------------------------- 1 | def fraction(promenade): 2 | l, m = 1, 0 3 | r, s = 0, 1 4 | 5 | for i in range(len(promenade)): 6 | if promenade[i]=="L": 7 | l, m = l+r, s+m 8 | else: 9 | r, s = l+r, s+m 10 | 11 | print(f"{l+r}/{m+s}") 12 | 13 | def _1b(): # LRL + LLLL = 4/5 14 | print("LRRR") 15 | 16 | def _1c(): 17 | print("999,999 Ls and 0 Rs") 18 | 19 | def _1d(): 20 | print("""No none do. The promenade before any choices has l,r,m,s equal to 1,0,0,1 respectively. 21 | When the next decision is an 'L', l=l+r and m=s+m; when it is 'R', r=l+r, s=s+m. 22 | Since both of these choices only add positive (including 0) integers together (since l,r,m,s are originally positive): 23 | l,r,m,s are always positive for any promenade. So every promenade of form (l+r)/(m+s) is positive.""") 24 | 25 | if __name__=='__main__': 26 | NAME = "" 27 | SCHOOL = "" 28 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 29 | promenade = input() 30 | fraction(promenade) -------------------------------------------------------------------------------- /2016/bio16q2.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | squares = defaultdict(int) 3 | 4 | def pos2d(pos1d): 5 | pos1d -= 1 6 | return (pos1d//5, pos1d%5) 7 | 8 | def migrate(pos2d): 9 | squares[pos2d] -= 4 10 | y, x = pos2d 11 | squares[(y+1, x)] += 1 12 | while squares[(y+1, x)] > 3: 13 | migrate((y+1,x)) 14 | squares[(y-1, x)] += 1 15 | while squares[(y-1, x)] > 3: 16 | migrate((y-1,x)) 17 | squares[(y, x+1)] += 1 18 | while squares[(y, x+1)] > 3: 19 | migrate((y,x+1)) 20 | squares[(y, x-1)] += 1 21 | while squares[(y, x-1)] > 3: 22 | migrate((y,x-1)) 23 | 24 | def step(pos1d): 25 | pos = pos2d(pos1d) 26 | squares[pos] += 1 27 | while squares[pos]>3: 28 | migrate(pos) 29 | 30 | def _2b(): 31 | print(7) # WRONG 16 32 | 33 | def _2c(): 34 | # 6,8,11,16, 18,21,1, 3 35 | print("6 3 8") 36 | print("2 3 5") 37 | starts = [1,3,6,8,11,16,18,21] 38 | total = 0 39 | for start in starts: 40 | for i in range(25): 41 | for j in range(25): 42 | if i==j: 43 | continue 44 | for k in range(25): 45 | seen = set() 46 | if k==j or k==i: 47 | continue 48 | seen.add(start) 49 | seen.add((start+i)%25) 50 | seen.add((start+i+j)%25) 51 | seen.add((start+i+j+k)%25) 52 | seen.add((start+i*2+j+k)%25) 53 | seen.add((start+i*2+j*2+k)%25) 54 | seen.add((start+i*2+j*2+k*2)%25) 55 | seen.add((start+i*3+j*2+k*2)%25) 56 | for s in starts: 57 | if s not in seen: 58 | total -= 1 59 | break 60 | total += 1 61 | print(total) 62 | 63 | def _2d(): 64 | print("""No you cannot always determine the landscape. This is because of the possibility of multiple migrations. 65 | For example take the end landscape, given that the person was added to position 8: 66 | 0 1 1 0 0 67 | 1 0 1 1 0 68 | 0 1 1 0 0 69 | 0 0 0 0 0 70 | 0 0 0 0 0 71 | The original landscape could have been either: 72 | 0 0 0 0 0 73 | 0 3 3 0 0 74 | 0 0 0 0 0 75 | 0 0 0 0 0 76 | 0 0 0 0 0 77 | or 78 | 0 1 1 0 0 79 | 1 0 0 1 0 80 | 0 1 1 0 0 81 | 0 0 0 0 0 82 | 0 0 0 0 0""") 83 | 84 | if __name__=='__main__': 85 | NAME = "" 86 | SCHOOL = "" 87 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 88 | p, s, n = (int(x) for x in input().split()) 89 | seq = [int(x) for x in input().split()] 90 | done = False 91 | 92 | while not done: 93 | for pos in seq: 94 | step(p) 95 | p += pos 96 | p-=1 97 | p %= 25 98 | p+=1 99 | n -= 1 100 | if n==0: 101 | done=True 102 | break 103 | for i in range(5): 104 | to_print = [] 105 | for j in range(5): 106 | to_print.append(str(squares[(i,j)])) 107 | print(" ".join(to_print)) -------------------------------------------------------------------------------- /2016/bio16q3v2.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | 3 | coins = [- 2**i for i in range(25)] 4 | coins2 = [-i for i in coins] 5 | def solve(start, end): 6 | len_primes = len(primes) 7 | lst = [0 for _ in range(len_primes)] 8 | lst[pos[start]] = 1 9 | for i in range(pos[start], pos[end]+1): 10 | c=primes[i] 11 | for coin in coins2: 12 | if coin>c: 13 | #print(c) 14 | break 15 | if c-coin not in primes: 16 | #print(c) 17 | continue 18 | if lst[pos[c-coin]]==0: 19 | continue 20 | if lst[i]==0: 21 | lst[i]=lst[pos[c-coin]]+1 22 | continue 23 | lst[i]=min(lst[pos[c-coin]]+1, lst[i]) 24 | for i in range(pos[start], pos[end]+1): 25 | c=primes[i] 26 | if c==29: print(c) 27 | for coin in coins: 28 | if c==29 and coin==-2: 29 | print(c-coin) 30 | if c-coin>l-1: 31 | #print(c) 32 | continue 33 | if c-coin not in primes: 34 | #print(c) 35 | continue 36 | if lst[pos[c-coin]]==0: 37 | continue 38 | if lst[i]==0: 39 | lst[i]=lst[pos[c-coin]]+1 40 | continue 41 | lst[i]=min(lst[pos[c-coin]]+1, lst[i]) 42 | 43 | print(primes) 44 | print(lst) 45 | print(lst[pos[end]]) 46 | 47 | 48 | if __name__=='__main__': 49 | NAME = "" 50 | SCHOOL = "" 51 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 52 | l, p, q = (int(x) for x in input().split()) 53 | with open("Primes.pickle", 'rb') as f: 54 | primes = [int(x) for x in pickle.load(f) if int(x) < l] 55 | pos = {primes[i]:i for i in range(len(primes))} 56 | solve(p,q) 57 | -------------------------------------------------------------------------------- /2017/bio17q1.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | from itertools import combinations_with_replacement as cr 3 | 4 | pairs = {"RR":"R","GG":"G","BB":"B", "RG":"B", "GR":"B","BR":"G", "RB":"G","GB":"R","BG":"R"} 5 | 6 | def finalColour(string): 7 | if len(string)==1: 8 | return string 9 | while len(string) > 1: 10 | temp = "" 11 | for i in range(len(string)-1): 12 | temp += pairs[string[i] + string[i+1]] 13 | string = temp 14 | return string 15 | 16 | def createRow(length, middle): 17 | # With chosen extreme left and right 18 | string = "B" + middle + "B" 19 | 20 | def d(): 21 | colours = ["B","R","G"] 22 | i = 4 23 | while True: 24 | i += 1 25 | b = True 26 | for x in cr(colours, i-2): 27 | print("".join(x)) 28 | if not b: 29 | break 30 | string = "B" + "".join(x) + "B" 31 | if finalColour(string) != "B": 32 | b= False 33 | if b: 34 | print(i) 35 | break 36 | 37 | if __name__ == '__main__': 38 | NAME = "" 39 | SCHOOL = "" 40 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 41 | while True: 42 | print("\n") 43 | string = input() 44 | # start = time() 45 | print(finalColour(string)) 46 | # print(time()-start) 47 | -------------------------------------------------------------------------------- /2017/bio17q2.py: -------------------------------------------------------------------------------- 1 | class Game(): 2 | 3 | def __init__(self, p1, m1, p2, m2, t): 4 | self.board = Board() 5 | self.board.squares[0].owner 6 | self.players = [Player(p1,m1, 0), Player(p2,m2, 1)] 7 | self.turns = t 8 | 9 | def run(self): 10 | currentTurn = 0 11 | for i in range(self.turns): 12 | # t<=60 so move is always available 13 | # Player.move() not only makes move but returns True/False for whether square has been won 14 | if not self.players[currentTurn].move(self.board): 15 | currentTurn += 1 16 | currentTurn %= 2 17 | self.result() 18 | 19 | def result(self): 20 | s = "" 21 | i = 0 22 | for square in self.board.squares: 23 | s += square.owner 24 | if i%5 == 4: 25 | s += "\n" 26 | i += 1 27 | print(s) 28 | print(len(self.players[0].wonSquares),len(self.players[1].wonSquares)) 29 | if all(self.board.squares[i].owner=="O" for i in range(25)): 30 | return True 31 | else: return False 32 | 33 | 34 | 35 | class Board(): 36 | 37 | def __init__(self): 38 | self.squares = [] 39 | for i in range(25): 40 | self.squares.append(Square()) 41 | 42 | def __str__(self): 43 | return str([self.squares[i].sides for i in range(25)]) 44 | 45 | 46 | class Square(): 47 | def __init__(self): 48 | # [[right, top, left, bottom]] 49 | self.sides = [0,0,0,0] 50 | self.owner = "*" 51 | 52 | def completeSquare(self,turn): 53 | if all(x==1 for x in self.sides): 54 | if turn == 0: 55 | self.owner = "X" 56 | else: self.owner = "O" 57 | return True 58 | 59 | class Player(): 60 | def __init__(self, position, modifier, playerNum): 61 | self.position = position 62 | self.modifier = modifier 63 | self.wonSquares = [] 64 | self.playerNum = playerNum 65 | 66 | def move(self, board): 67 | 68 | self.position += self.modifier 69 | while True: 70 | if self.position > 36: 71 | self.position %= 36 72 | P = self.position - 1 73 | column = P % 6 74 | row = P // 6 75 | 76 | if self.playerNum == 1: 77 | 78 | ### Upwards line 79 | if row == 0: pass 80 | else: 81 | if column == 5: 82 | sq = board.squares[row*5 + column - 5 - 1] 83 | if sq.sides[0] == 0: 84 | sq.sides[0] = 1 85 | if sq.completeSquare(self.playerNum): 86 | self.wonSquares.append(row * 5 + column - 5 - 1) 87 | return True 88 | return False 89 | # Drawing a right line at the bottom edge 90 | elif column == 0: 91 | sq = board.squares[row*5 + column - 5] 92 | if sq.sides[2] == 0: 93 | sq.sides[2] = 1 94 | if sq.completeSquare(self.playerNum): 95 | self.wonSquares.append(row * 5 + column - 5) 96 | return True 97 | return False 98 | # Drawing a line in the centre 99 | else: 100 | new = False 101 | changed = False 102 | sq = board.squares[row*5 + column - 5 - 1] 103 | if sq.sides[0] == 0: 104 | sq.sides[0] = 1 105 | changed = True 106 | if sq.completeSquare(self.playerNum): 107 | self.wonSquares.append(row * 5 + column - 5 - 1) 108 | new = True 109 | sq = board.squares[row*5 + column - 5] 110 | if sq.sides[2] == 0: 111 | sq.sides[2] = 1 112 | changed = True 113 | if sq.completeSquare(self.playerNum): 114 | self.wonSquares.append(row * 5 + column - 5) 115 | new = True 116 | if new: 117 | return True 118 | if changed: 119 | return False 120 | 121 | # If on the left, cannot go left 122 | ### Left line 123 | if column == 0: pass 124 | else: 125 | if row == 0: 126 | sq = board.squares[row*5 + column - 1] 127 | if sq.sides[1] == 0: 128 | sq.sides[1] = 1 129 | if sq.completeSquare(self.playerNum): 130 | self.wonSquares.append(row * 5 + column - 1) 131 | return True 132 | return False 133 | # Drawing a left line at the bottom edge 134 | elif row == 5: 135 | sq = board.squares[row*5 + column - 5 - 1] 136 | if sq.sides[3] == 0: 137 | sq.sides[3] = 1 138 | if sq.completeSquare(self.playerNum): 139 | self.wonSquares.append(row * 5 + column - 5 - 1) 140 | return True 141 | return False 142 | # Drawing a line in the centre 143 | else: 144 | new = False 145 | changed = False 146 | sq = board.squares[row*5 + column - 1] 147 | if sq.sides[1] == 0: 148 | sq.sides[1] = 1 149 | changed = True 150 | if sq.completeSquare(self.playerNum): 151 | self.wonSquares.append(row * 5 + column - 1) 152 | new = True 153 | sq = board.squares[row*5 + column - 5 - 1] 154 | if sq.sides[3] == 0: 155 | sq.sides[3] = 1 156 | changed = True 157 | if sq.completeSquare(self.playerNum): 158 | self.wonSquares.append(row * 5 + column - 5 - 1) 159 | new = True 160 | if new: 161 | return True 162 | if changed: 163 | return False 164 | 165 | # If on the bottom , cannot go bottom 166 | ### Downwards line 167 | if row == 5: pass 168 | else: 169 | if column == 0: 170 | sq = board.squares[row*5 + column] 171 | if sq.sides[2] == 0: 172 | sq.sides[2] = 1 173 | if sq.completeSquare(self.playerNum): 174 | self.wonSquares.append(row * 5 + column) 175 | return True 176 | return False 177 | # Drawing a right line at the bottom edge 178 | elif column == 5: 179 | sq = board.squares[row*5 + column - 1] 180 | if sq.sides[0] == 0: 181 | sq.sides[0] = 1 182 | if sq.completeSquare(self.playerNum): 183 | self.wonSquares.append(row * 5 + column - 1) 184 | return True 185 | return False 186 | # Drawing a line in the centre 187 | else: 188 | new = False 189 | changed = False 190 | sq = board.squares[row*5 + column] 191 | if sq.sides[2] == 0: 192 | sq.sides[2] = 1 193 | changed = True 194 | if sq.completeSquare(self.playerNum): 195 | self.wonSquares.append(row * 5 + column) 196 | new = True 197 | sq = board.squares[row*5 + column - 1] 198 | if sq.sides[0] == 0: 199 | sq.sides[0] = 1 200 | changed = True 201 | if sq.completeSquare(self.playerNum): 202 | self.wonSquares.append(row * 5 + column - 1) 203 | new = True 204 | if new: 205 | return True 206 | if changed: 207 | return False 208 | 209 | ### Line to the right 210 | # Cant draw a line from right edge 211 | if column == 5: pass 212 | else: 213 | # Drawing a right line at the top edge 214 | if row == 0: 215 | sq = board.squares[row*5 + column] 216 | sq.sides 217 | if sq.sides[1] == 0: 218 | sq.sides[1] = 1 219 | if sq.completeSquare(self.playerNum): 220 | self.wonSquares.append(row * 5 + column) 221 | return True 222 | return False 223 | # Drawing a right line at the bottom edge 224 | elif row == 5: 225 | sq = board.squares[row*5 + column - 5] 226 | if sq.sides[3] == 0: 227 | sq.sides[3] = 1 228 | if sq.completeSquare(self.playerNum): 229 | self.wonSquares.append(row * 5 + column - 5) 230 | return True 231 | return False 232 | # Drawing a line in the centre 233 | else: 234 | new = False 235 | changed = False 236 | sq = board.squares[row*5 + column - 5] 237 | if sq.sides[3] == 0: 238 | sq.sides[3] = 1 239 | changed = True 240 | if sq.completeSquare(self.playerNum): 241 | self.wonSquares.append(row * 5 + column - 5) 242 | new = True 243 | sq = board.squares[row*5 + column] 244 | if sq.sides[1] == 0: 245 | sq.sides[1] = 1 246 | changed = True 247 | if sq.completeSquare(self.playerNum): 248 | self.wonSquares.append(row * 5 + column) 249 | new = True 250 | if new: 251 | return True 252 | if changed: 253 | return False 254 | 255 | else: 256 | ### Upwards line 257 | if row == 0: pass 258 | else: 259 | if column == 5: 260 | sq = board.squares[row*5 + column - 5 - 1] 261 | if sq.sides[0] == 0: 262 | sq.sides[0] = 1 263 | if sq.completeSquare(self.playerNum): 264 | self.wonSquares.append(row * 5 + column - 5 - 1) 265 | return True 266 | return False 267 | # Drawing a right line at the bottom edge 268 | elif column == 0: 269 | sq = board.squares[row*5 + column - 5] 270 | if sq.sides[2] == 0: 271 | sq.sides[2] = 1 272 | if sq.completeSquare(self.playerNum): 273 | self.wonSquares.append(row * 5 + column - 5) 274 | return True 275 | return False 276 | # Drawing a line in the centre 277 | else: 278 | new = False 279 | changed = False 280 | sq = board.squares[row*5 + column - 5 - 1] 281 | if sq.sides[0] == 0: 282 | sq.sides[0] = 1 283 | changed = True 284 | if sq.completeSquare(self.playerNum): 285 | self.wonSquares.append(row * 5 + column - 5 - 1) 286 | new = True 287 | sq = board.squares[row*5 + column - 5] 288 | if sq.sides[2] == 0: 289 | sq.sides[2] = 1 290 | changed = True 291 | if sq.completeSquare(self.playerNum): 292 | self.wonSquares.append(row * 5 + column - 5) 293 | new = True 294 | if new: 295 | return True 296 | if changed: 297 | return False 298 | 299 | ### Line to the right 300 | # Cant draw a line from right edge 301 | if column == 5: pass 302 | else: 303 | # Drawing a right line at the top edge 304 | if row == 0: 305 | sq = board.squares[row*5 + column] 306 | sq.sides 307 | if sq.sides[1] == 0: 308 | sq.sides[1] = 1 309 | if sq.completeSquare(self.playerNum): 310 | self.wonSquares.append(row * 5 + column) 311 | return True 312 | return False 313 | # Drawing a right line at the bottom edge 314 | elif row == 5: 315 | sq = board.squares[row*5 + column - 5] 316 | if sq.sides[3] == 0: 317 | sq.sides[3] = 1 318 | if sq.completeSquare(self.playerNum): 319 | self.wonSquares.append(row * 5 + column - 5) 320 | return True 321 | return False 322 | # Drawing a line in the centre 323 | else: 324 | new = False 325 | changed = False 326 | sq = board.squares[row*5 + column - 5] 327 | if sq.sides[3] == 0: 328 | sq.sides[3] = 1 329 | changed = True 330 | if sq.completeSquare(self.playerNum): 331 | self.wonSquares.append(row * 5 + column - 5) 332 | new = True 333 | sq = board.squares[row*5 + column] 334 | if sq.sides[1] == 0: 335 | sq.sides[1] = 1 336 | changed = True 337 | if sq.completeSquare(self.playerNum): 338 | self.wonSquares.append(row * 5 + column) 339 | new = True 340 | if new: 341 | return True 342 | if changed: 343 | return False 344 | 345 | # If on the bottom , cannot go bottom 346 | ### Downwards line 347 | if row == 5: pass 348 | else: 349 | if column == 0: 350 | sq = board.squares[row*5 + column] 351 | if sq.sides[2] == 0: 352 | sq.sides[2] = 1 353 | if sq.completeSquare(self.playerNum): 354 | self.wonSquares.append(row * 5 + column) 355 | return True 356 | return False 357 | # Drawing a right line at the bottom edge 358 | elif column == 5: 359 | sq = board.squares[row*5 + column - 1] 360 | if sq.sides[0] == 0: 361 | sq.sides[0] = 1 362 | if sq.completeSquare(self.playerNum): 363 | self.wonSquares.append(row * 5 + column - 1) 364 | return True 365 | return False 366 | # Drawing a line in the centre 367 | else: 368 | new = False 369 | changed = False 370 | sq = board.squares[row*5 + column] 371 | if sq.sides[2] == 0: 372 | sq.sides[2] = 1 373 | changed = True 374 | if sq.completeSquare(self.playerNum): 375 | self.wonSquares.append(row * 5 + column) 376 | new = True 377 | sq = board.squares[row*5 + column - 1] 378 | if sq.sides[0] == 0: 379 | sq.sides[0] = 1 380 | changed = True 381 | if sq.completeSquare(self.playerNum): 382 | self.wonSquares.append(row * 5 + column - 1) 383 | new = True 384 | if new: 385 | return True 386 | if changed: 387 | return False 388 | 389 | # If on the left, cannot go left 390 | ### Left line 391 | if column == 0: pass 392 | else: 393 | if row == 0: 394 | sq = board.squares[row*5 + column - 1] 395 | if sq.sides[1] == 0: 396 | sq.sides[1] = 1 397 | if sq.completeSquare(self.playerNum): 398 | self.wonSquares.append(row * 5 + column - 1) 399 | return True 400 | return False 401 | # Drawing a right line at the bottom edge 402 | elif row == 5: 403 | sq = board.squares[row*5 + column - 5 - 1] 404 | if sq.sides[3] == 0: 405 | sq.sides[3] = 1 406 | if sq.completeSquare(self.playerNum): 407 | self.wonSquares.append(row * 5 + column - 5 - 1) 408 | return True 409 | return False 410 | # Drawing a line in the centre 411 | else: 412 | new = False 413 | changed = False 414 | sq = board.squares[row*5 + column - 1] 415 | if sq.sides[1] == 0: 416 | sq.sides[1] = 1 417 | changed = True 418 | if sq.completeSquare(self.playerNum): 419 | self.wonSquares.append(row * 5 + column - 1) 420 | new = True 421 | sq = board.squares[row*5 + column - 5 - 1] 422 | if sq.sides[3] == 0: 423 | sq.sides[3] = 1 424 | changed = True 425 | if sq.completeSquare(self.playerNum): 426 | self.wonSquares.append(row * 5 + column - 5 - 1) 427 | new = True 428 | if new: 429 | return True 430 | if changed: 431 | return False 432 | 433 | 434 | 435 | 436 | self.position += 1 437 | 438 | 439 | def c(): 440 | ans = 0 441 | for p in range(1,37): 442 | for m in range(1,36): 443 | game = Game(p, m, p, m, 60) 444 | game.run() 445 | if game.result(): 446 | ans += 1 447 | print(ans) 448 | 449 | if __name__ == '__main__': 450 | NAME = "" 451 | SCHOOL = "" 452 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 453 | p1, m1, p2, m2, t = input().split() 454 | game = Game(int(p1), int(m1), int(p2), int(m2), int(t)) 455 | game.run() 456 | -------------------------------------------------------------------------------- /2017/bio17q3v2.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | from collections import defaultdict 3 | import time 4 | 5 | NAME = "" 6 | SCHOOL = "" 7 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 8 | p,i,n,w = (int(x) for x in input().split()) 9 | 10 | start = time.time() 11 | """ 12 | weights = [x for x in range(1,i+1)] 13 | 14 | for a in range(1,n-p+2): 15 | for x in product(weights, repeat= a): 16 | if sum(x) == w: 17 | combs.add(tuple(sorted(x))) 18 | print(combs) 19 | 20 | 21 | distribution = 0 22 | 23 | for x in product(combs, repeat=p): 24 | if sum(len(y) for y in x) == n: 25 | distribution += 1 26 | 27 | print(distribution) 28 | """ 29 | 30 | 31 | def findNum(remainingNum, currentSet=[[]], maximum=100000, wantedLength = 0): 32 | x = [] 33 | #print((remainingNum,currentSet)) 34 | if remainingNum == 0: 35 | if wantedLength: 36 | if len(currentSet[0]) != wantedLength: 37 | return None 38 | return currentSet 39 | if wantedLength: 40 | if len(currentSet) > wantedLength: 41 | return 42 | for i in range(1,min(maximum + 1,remainingNum + 1)): 43 | y = [] 44 | for a in currentSet: 45 | y.append(a + [i]) 46 | z = findNum(remainingNum-i, y, maximum, wantedLength) 47 | if wantedLength and z is None: 48 | continue 49 | else: 50 | x += z 51 | 52 | 53 | #print(x) 54 | 55 | return x 56 | 57 | temp = findNum(w, [[]], i) 58 | 59 | combs = [] 60 | for x in temp: 61 | if sorted(x) not in combs: 62 | combs.append(x) 63 | 64 | distribution = findNum(n, wantedLength = p) 65 | 66 | print(time.time()-start) 67 | 68 | lengths = defaultdict(int) 69 | for x in combs: 70 | lengths[len(x)] += 1 71 | 72 | total = 0 73 | for x in distribution: 74 | c = 1 75 | for y in x: 76 | c *= lengths[y] 77 | total += c 78 | 79 | print(total) 80 | print(time.time()-start) 81 | #for i in range(p) 82 | 83 | -------------------------------------------------------------------------------- /2018/bio2018q1.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def calculator(interest, repay): 4 | 5 | amount = 10000 6 | total_repaid = 0 7 | 8 | while amount > 0: 9 | interest_month = math.ceil((interest / 100) * amount) 10 | amount += interest_month 11 | repaid = math.ceil((repay / 100) * amount) 12 | repaid = min(max(5000, repaid), amount) 13 | total_repaid += repaid 14 | amount -= repaid 15 | 16 | print("%.2f" % (total_repaid / 100)) 17 | 18 | def b(): 19 | print(5) 20 | 21 | def c(): 22 | maxPaid = 0 23 | maxI = 0 24 | maxR = 0 25 | for i in range(0,101): 26 | for r in range(0,101): 27 | temp = calculator(i,r) 28 | if temp > maxPaid: 29 | maxPaid = temp 30 | maxI = i 31 | maxR = r 32 | print((maxI, maxR)) 33 | 34 | if __name__ == '__main__': 35 | NAME = "" 36 | SCHOOL = "" 37 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 38 | while True: 39 | i, r = (int(x) for x in input().split()) 40 | calculator(i,r) 41 | 42 | -------------------------------------------------------------------------------- /2018/bio2018q2.py: -------------------------------------------------------------------------------- 1 | 2 | def createSecondDial(n): 3 | alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 4 | second_Dial = "" 5 | a = -1 6 | used = "" 7 | for i in range(26): 8 | a += n 9 | a %= 26-i 10 | second_Dial += alphabet[a] 11 | alphabet = alphabet.replace(alphabet[a], "") 12 | a -= 1 13 | return second_Dial 14 | 15 | def encrypt(string, second_Dial): 16 | encrypted = "" 17 | first_Dial = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 18 | for i in range(len(string)): 19 | encrypted += second_Dial[(first_Dial.index(string[i])+i)%26] 20 | return encrypted 21 | 22 | def c(): 23 | for i in range(1,1000001): 24 | second_Dial = createSecondDial(i) 25 | x = encrypt("ABCDEFGHIJKLMNOPQRSTUVWXYZ", second_Dial) 26 | if sorted(x) == list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"): 27 | print(second_Dial) 28 | if __name__ == '__main__': 29 | print("Name: Ryuichi Leo Takashige\nSchool: Westminster School\n") 30 | n, string = input().split() 31 | second_Dial = createSecondDial(int(n)) 32 | print(second_Dial[:6]) 33 | print(encrypt(string, second_Dial)) 34 | 35 | 36 | 37 | """ 38 | 39 | def createSecondDial2(n): 40 | alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 41 | second_Dial = "" 42 | a = -1 43 | used = "" 44 | for i in range(26): 45 | for _ in range(n): 46 | a += 1 47 | a%=26 48 | while alphabet[a] in used: 49 | a+=1 50 | a%=26 51 | second_Dial += alphabet[a] 52 | used += alphabet[a] 53 | return second_Dial 54 | 55 | """ 56 | -------------------------------------------------------------------------------- /2018/bio2018q3ownImproved.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | from collections import defaultdict 3 | import time 4 | 5 | 6 | @lru_cache(maxsize=None) 7 | def mid(middle, left, right): 8 | return middle < max(left, right) and middle > min(left,right) 9 | 10 | 11 | connections = defaultdict(set) 12 | seen = set() 13 | 14 | 15 | @lru_cache(maxsize=None) 16 | def createEquivalences(string): 17 | seen.add(string) 18 | if len(string) < 3: 19 | connections[string] = set() 20 | 21 | for i in range(len(string)-1): 22 | if i: 23 | if mid(string[i-1], string[i], string[i+1]): 24 | connections[string].add(string[:i]+string[i+1] + string[i] + string[i+2:]) 25 | continue 26 | if i!=len(string)-2: 27 | if mid(string[i+2], string[i], string[i+1]): 28 | connections[string].add(string[:i]+string[i+1] + string[i] + string[i+2:]) 29 | continue 30 | for x in connections[string]: 31 | if x not in seen: 32 | createEquivalences(x) 33 | 34 | 35 | NAME = "" 36 | SCHOOL = "" 37 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 38 | input() 39 | string = input() 40 | start = time.time() 41 | createEquivalences(string) 42 | 43 | 44 | def bfs(source): 45 | seen = set() 46 | queue = [] 47 | depths = defaultdict(int) 48 | queue.append((source,0)) 49 | seen.add(source) 50 | 51 | while queue: 52 | x, depth = queue.pop(0) 53 | depths[x] = depth 54 | 55 | for c in connections[x]: 56 | if c not in seen: 57 | queue.append((c,depth+1)) 58 | seen.add(x) 59 | 60 | return depths 61 | 62 | 63 | print(max(bfs(string).values())) 64 | print(time.time()-start) 65 | -------------------------------------------------------------------------------- /2019/bio19q1.py: -------------------------------------------------------------------------------- 1 | def is_palindrome(n): 2 | if n[::-1] == n: return True 3 | else: return False 4 | 5 | def next_palindrome(n): 6 | n+=1 7 | while True: 8 | strn = str(n) 9 | if is_palindrome(strn): 10 | return n 11 | b = True 12 | for i in range(len(strn)//2): 13 | if is_palindrome(strn): 14 | return int(strn) 15 | if b: 16 | if int(strn[i]) >= int(strn[-i-1]): 17 | if i != 0: 18 | temp = strn[:-i-1] + strn[i] + strn[-i:] 19 | else: 20 | temp = strn[:-i-1] + strn[i] 21 | strn = temp 22 | else: 23 | n = int(strn) 24 | x = 10 + int(strn[i]) - int(strn[-i-1]) 25 | n += x * 10**i 26 | strn = str(n) 27 | b = False 28 | n = int(strn) 29 | 30 | 31 | def find_all_palindromes(stop): 32 | lst = [] 33 | for i in range(0,stop): 34 | if is_palindrome(str(i)): 35 | lst.append(i) 36 | print(len(lst)) 37 | ans = [] 38 | for i in range(len(lst)): 39 | print(len(ans)) 40 | for j in range(i,len(lst)): 41 | k = lst[i]+lst[j] 42 | if k > stop: 43 | break 44 | if k not in ans: 45 | ans.append(k) 46 | print(stop-len(ans)) 47 | # Buffers at 90070 so stands to reason that the answer is 9030. 48 | 49 | 50 | if __name__ == '__main__': 51 | NAME = "" 52 | SCHOOL = "" 53 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 54 | while True: 55 | n = input() 56 | print(next_palindrome(int(n))) 57 | -------------------------------------------------------------------------------- /2019/bio19q2.py: -------------------------------------------------------------------------------- 1 | class Grid(): 2 | 3 | def __init__(self, trail_memory): 4 | self.memory = trail_memory 5 | self.trail = [] 6 | 7 | def update_grid(self, former_position): 8 | self.trail.append(former_position) 9 | if len(self.trail) > self.memory: 10 | del self.trail[0] 11 | 12 | def valid_move(self, new_position): 13 | if new_position in self.trail: 14 | return False 15 | else: 16 | return True 17 | 18 | class Agent(): 19 | 20 | def __init__(self, t, instructions, moves): 21 | self.instructions = instructions * (moves//len(instructions)) + instructions[:moves%len(instructions)] 22 | self.directionList = ["F", "R", "B", "L"] 23 | self.direction = 0 24 | self.position = (0,0) 25 | self.grid = Grid(t) 26 | self.realDirectionDict = [(0,1), (1,0), (0,-1), (-1,0)] 27 | 28 | def explore(self): 29 | for instruction in self.instructions: 30 | moveable = 4 31 | while True: 32 | if moveable == 0: 33 | break 34 | new_direction = self.direction + self.directionList.index(instruction) 35 | new_direction %= 4 36 | new_position = self.change(self.position, self.realDirectionDict[new_direction]) 37 | if self.grid.valid_move(new_position): 38 | self.grid.update_grid(self.position) 39 | self.position = new_position 40 | self.direction = new_direction 41 | break 42 | else: 43 | self.direction += 1 44 | self.direction %= 4 45 | moveable -= 1 46 | print(self.position) 47 | 48 | def change(self, pos, move): 49 | return (pos[0] + move[0], pos[1] + move[1]) 50 | 51 | def c(self): 52 | pos = [] 53 | for x in range(-10,11): 54 | for y in range(-10,11): 55 | pos.append((x,y)) 56 | 57 | ans = 0 58 | 59 | while True: 60 | if self.position in pos: 61 | pos.remove(self.position) 62 | if len(pos) == 0: 63 | break 64 | 65 | ans += 1 66 | moveable = 4 67 | while True: 68 | if moveable == 0: 69 | break 70 | new_direction = self.direction + self.directionList.index("L") 71 | new_direction %= 4 72 | new_position = self.change(self.position, self.realDirectionDict[new_direction]) 73 | if self.grid.valid_move(new_position): 74 | self.grid.update_grid(self.position) 75 | self.position = new_position 76 | self.direction = new_direction 77 | break 78 | else: 79 | self.direction += 1 80 | self.direction %= 4 81 | moveable -= 1 82 | print(ans) 83 | 84 | def d(self): 85 | for i in range(100): 86 | for instruction in self.instructions: 87 | moveable = 4 88 | while True: 89 | if moveable == 0: 90 | return False 91 | new_direction = self.direction + self.directionList.index(instruction) 92 | new_direction %= 4 93 | new_position = self.change(self.position, self.realDirectionDict[new_direction]) 94 | if self.grid.valid_move(new_position): 95 | self.grid.update_grid(self.position) 96 | self.position = new_position 97 | self.direction = new_direction 98 | break 99 | else: 100 | self.direction += 1 101 | self.direction %= 4 102 | moveable -= 1 103 | 104 | return True 105 | 106 | 107 | def c(): 108 | agent = Agent(1000000, "L", 1) 109 | agent.c() 110 | # Result 440 111 | 112 | def d(): 113 | t = 1300 114 | while True: 115 | agent = Agent(t, "LLRFFF", 6) 116 | if agent.d(): 117 | t -= 1 118 | elif t < 100: 119 | print("Fail") 120 | break 121 | else: 122 | print(t) 123 | break 124 | 125 | if __name__ == '__main__': 126 | NAME = "" 127 | SCHOOL = "" 128 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 129 | while True: 130 | print("\n") 131 | t, i, m = input().split() 132 | t, m = int(t), int(m) 133 | agent = Agent(t, i, m) 134 | agent.explore() 135 | 136 | 137 | -------------------------------------------------------------------------------- /2019/bio19q3.py: -------------------------------------------------------------------------------- 1 | # Uses memoization for speed. 2 | # Did you know that you can't memoize using a list as a key? My solution here was to use * to "unzip" it". 3 | 4 | from functools import lru_cache 5 | import time 6 | 7 | NAME = "" 8 | SCHOOL = "" 9 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 10 | l, p = input().split() 11 | l = int(l) 12 | wanted = l 13 | 14 | 15 | @lru_cache(maxsize=None) 16 | def valid(_tuple): 17 | if _tuple[0] < _tuple[1] and _tuple[2] < _tuple[1]: 18 | return True 19 | if _tuple[0] > _tuple[1]: 20 | return True 21 | return False 22 | 23 | 24 | def valid2(_set): 25 | smallest = _set[0] 26 | for i in range(1, len(_set)): 27 | if _set[i] < smallest: 28 | smallest = _set[i] 29 | else: 30 | for j in range(i+1,len(_set)): 31 | if _set[j] > _set[i]: 32 | return False 33 | return True 34 | 35 | memo = {} 36 | 37 | def count(length, letters, smallest, afterSmallest): 38 | if length==wanted: 39 | return 1 40 | total = 0 41 | for letter in letters: 42 | 43 | _smallest = smallest 44 | _afterSmallest = afterSmallest 45 | if letter < smallest: 46 | _smallest = letter 47 | elif afterSmallest == None: 48 | _afterSmallest = letter 49 | elif letter < afterSmallest: 50 | _afterSmallest = letter 51 | else: 52 | continue 53 | 54 | if length==1: 55 | temp = letters.copy() 56 | temp.remove(letter) 57 | if (length + 1, *temp, _smallest, _afterSmallest) in memo: 58 | total += memo[(length + 1, *temp, _smallest, _afterSmallest)] 59 | else: 60 | x = count(length + 1, temp, _smallest, _afterSmallest) 61 | memo[(length + 1, *temp, _smallest, _afterSmallest)] = x 62 | total += x 63 | continue 64 | # elif valid((string[-2],string[-1],letter)): 65 | temp = letters.copy() 66 | temp.remove(letter) 67 | #if valid2(string + [letter]): 68 | if (length + 1, *temp, _smallest, _afterSmallest) in memo: 69 | total += memo[(length + 1, *temp, _smallest, _afterSmallest)] 70 | else: 71 | x = count(length + 1, temp, _smallest, _afterSmallest) 72 | memo[(length + 1, *temp, _smallest, _afterSmallest)] = x 73 | total += x 74 | return total 75 | 76 | letters = set(x for x in range(l)) 77 | used = list(ord(x)-ord("A") for x in p) 78 | 79 | smallest = min(used) 80 | y = used.index(smallest) 81 | afterSmallest = None 82 | if y != len(used) -1: 83 | afterSmallest = 50 84 | for a in range(y,len(used)): 85 | if used[a] < afterSmallest: 86 | afterSmallest = used[a] 87 | 88 | for x in used: 89 | letters.remove(x) 90 | if len(used) > 2: 91 | if valid2(used): 92 | start = time.time() 93 | print(count(len(used), letters, smallest, afterSmallest)) 94 | print(time.time()-start) 95 | else: 96 | print(0) 97 | else: 98 | start = time.time() 99 | print(count(len(used), letters, smallest, afterSmallest)) 100 | print(time.time()-start) -------------------------------------------------------------------------------- /2020/bio20q1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Q1s are usually not this implementation-heavy, but here we are. 3 | I think you just have to be rigorous for this, and I found it was easiest to split up the look and say functions. 4 | While it is a bit long, each step is very simple, so I haven't commented them. 5 | Please do let me know if you have a shorter solution, though! 6 | """ 7 | 8 | 9 | def look(_roman_numeral): 10 | current_letter = None 11 | letters = [] 12 | occurrences = [] 13 | for x in _roman_numeral: 14 | if x != current_letter: 15 | current_letter = x 16 | letters.append(current_letter) 17 | occurrences.append(1) 18 | else: 19 | occurrences[-1] += 1 20 | return letters, occurrences 21 | 22 | 23 | def roman_numeral(n): 24 | string = "" 25 | thousands = n//1000 26 | string += "M" * thousands 27 | n -= thousands * 1000 28 | if n >= 900: 29 | string += "CM" 30 | n -= 900 31 | if n >= 500: 32 | string += "D" 33 | n -= 500 34 | if n >= 400: 35 | string += "CD" 36 | n -= 400 37 | hundreds = n//100 38 | string += "C" * hundreds 39 | n -= hundreds * 100 40 | if n >= 90: 41 | string += "XC" 42 | n -= 90 43 | if n >= 50: 44 | string += "L" 45 | n -= 50 46 | if n >= 40: 47 | string += "XL" 48 | n -= 40 49 | tens = n//10 50 | string += "X" * tens 51 | n -= tens * 10 52 | if n == 9: 53 | string += "IX" 54 | return string 55 | if n >= 5: 56 | string += "V" 57 | n -= 5 58 | if n == 4: 59 | string += "IV" 60 | return string 61 | units = n 62 | string += "I" * units 63 | return string 64 | 65 | 66 | def say(letters, occurrences): 67 | string = "" 68 | for i in range(len(letters)): 69 | string += roman_numeral(occurrences[i]) + letters[i] 70 | return string 71 | 72 | 73 | def look_and_say(numeral, n): 74 | n = int(n) 75 | for i in range(n): 76 | letters, occurrences = look(numeral) 77 | numeral = say(letters, occurrences) 78 | iCount = 0 79 | vCount = 0 80 | for letter in numeral: 81 | if letter == "I": 82 | iCount += 1 83 | elif letter == "V": 84 | vCount += 1 85 | print(f"{iCount} {vCount}") 86 | return numeral 87 | 88 | 89 | order = ["I", "V", "X", "L", "C", "D", "M"] 90 | 91 | 92 | def valid(roman_numeral): 93 | if "IC" in roman_numeral or "IM" in roman_numeral or "IL" in roman_numeral or "ID" in roman_numeral or roman_numeral == "IIX": 94 | return False 95 | for i in range(len(roman_numeral)-3): 96 | if roman_numeral[i] == roman_numeral[i+1] == roman_numeral[i+2] == roman_numeral[i+3]: 97 | return False 98 | if order.index(roman_numeral[i + 3]) > order.index(roman_numeral[i]): 99 | return False 100 | if roman_numeral[i] == "I" and (roman_numeral[i+1] == "X" or roman_numeral[i+1] == "V") and roman_numeral[i+2] == "I": 101 | return False 102 | if roman_numeral[i] == "I" and roman_numeral[i+1] == "I" and (roman_numeral[i+2] == "V" or roman_numeral[i+2] == "X" or roman_numeral[i+2] == "C" or roman_numeral[i+2] == "M"): 103 | return False 104 | if roman_numeral[i+1] == "I" and (roman_numeral[i+2] == "X" or roman_numeral[i+2] == "V") and roman_numeral[i+3] == "I": 105 | return False 106 | if roman_numeral[i+1] == "I" and roman_numeral[i+2] == "I" and (roman_numeral[i+3] == "V" or roman_numeral[i+3] == "X" or roman_numeral[i+3] == "C" or roman_numeral[i+3] == "M"): 107 | return False 108 | else: return True 109 | 110 | 111 | def b(): 112 | for i in range(1,4000): 113 | temp = look_and_say(roman_numeral(i),1) 114 | if valid(temp): 115 | print(temp) 116 | 117 | 118 | def c(): 119 | lst = [] 120 | for i in range(1,4000): 121 | temp = look_and_say(roman_numeral(i),1) 122 | if temp not in lst: 123 | lst.append(temp) 124 | print(len(lst)) 125 | 126 | 127 | if __name__ == '__main__': 128 | NAME = "" 129 | SCHOOL = "" 130 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 131 | while True: 132 | print("\n") 133 | numeral, n = input().split() 134 | look_and_say(numeral, n) 135 | -------------------------------------------------------------------------------- /2020/bio20q2.py: -------------------------------------------------------------------------------- 1 | alphabet = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 2 | 3 | 4 | class Room(): 5 | def __init__(self, roomLetter): 6 | self.Letter = roomLetter 7 | self.connectedRooms = [] 8 | self.visited = 0 9 | self.connectionUsed = [] 10 | 11 | 12 | class Spy(): 13 | def __init__(self, plan, r): 14 | self.plan = plan 15 | self.unexploredRooms = alphabet[:r] 16 | self.nonPlanRooms = [] 17 | for room in self.unexploredRooms: 18 | self.nonPlanRooms.append(room) 19 | self.ans = self.rooms = [] 20 | 21 | def construct_map(self): 22 | rooms = [] 23 | chosen = [] 24 | while True: 25 | i = 0 26 | usedRooms = [room.Letter for room in rooms] 27 | if len(self.plan)== 0: 28 | for room in chosen: 29 | self.unexploredRooms.remove(room) 30 | if self.unexploredRooms[0] in usedRooms: 31 | rooms[usedRooms.index(self.unexploredRooms[0])].connectedRooms.append(self.unexploredRooms[1]) 32 | else: 33 | rooms.append(Room(self.unexploredRooms[0])) 34 | rooms[-1].connectedRooms.append(self.unexploredRooms[1]) 35 | if self.unexploredRooms[1] in usedRooms: 36 | rooms[usedRooms.index(self.unexploredRooms[1])].connectedRooms.append(self.unexploredRooms[0]) 37 | else: 38 | rooms.append(Room(self.unexploredRooms[1])) 39 | rooms[-1].connectedRooms.append(self.unexploredRooms[0]) 40 | break 41 | while True: 42 | if self.nonPlanRooms[i] not in self.plan: 43 | break 44 | else: i += 1 45 | if self.nonPlanRooms[i] not in usedRooms: 46 | first_room = Room(self.nonPlanRooms[i]) 47 | chosen.append(self.nonPlanRooms[i]) 48 | first_room.connectedRooms.append(self.plan[0]) 49 | rooms.append(first_room) 50 | else: 51 | chosen.append(self.nonPlanRooms[i]) 52 | rooms[usedRooms.index(self.nonPlanRooms[i])].connectedRooms.append(self.plan[0]) 53 | 54 | if self.plan[0] in usedRooms: 55 | rooms[usedRooms.index(self.plan[0])].connectedRooms.append(self.nonPlanRooms[i]) 56 | else: 57 | second_room = Room(self.plan[0]) 58 | second_room.connectedRooms.append(self.nonPlanRooms[i]) 59 | rooms.append(second_room) 60 | del self.plan[0] 61 | del self.nonPlanRooms[i] 62 | cR = [room.connectedRooms for room in rooms] 63 | rL = [room.Letter for room in rooms] 64 | ##################################################### 65 | self.ans = [''.join(s) for s in [sorted(connections) for connections, roomLetters in sorted(zip(cR, rL), key=lambda pair: pair[1])]] 66 | for line in self.ans: 67 | print(line) 68 | 69 | for room in rooms: 70 | room.connectedRooms = sorted(room.connectedRooms) 71 | 72 | self.rooms = [room for room, roomLetter in sorted(zip(rooms, [room.Letter for room in rooms]), key=lambda pair: pair[1])] 73 | 74 | def move(self, n): 75 | for room in self.rooms: 76 | room.visited = 0 77 | room.connectionUsed = [] 78 | for i in range(len(room.connectedRooms)): 79 | room.connectionUsed.append(0) 80 | currentLetter = "A" 81 | nextLetter = "" 82 | for moves in range(n): 83 | currentRoom = self.rooms[alphabet.index(currentLetter)] 84 | currentRoom.visited += 1 85 | if currentRoom.visited % 2 == 1: 86 | nextLetter = currentRoom.connectedRooms[0] 87 | currentRoom.connectionUsed[0] += 1 88 | else: 89 | for i in range(len(currentRoom.connectionUsed)): 90 | if currentRoom.connectionUsed[i] % 2 == 1: 91 | if i == len(currentRoom.connectionUsed) -1: 92 | nextLetter = currentRoom.connectedRooms[i] 93 | currentRoom.connectionUsed[i] += 1 94 | else: 95 | nextLetter = currentRoom.connectedRooms[i+1] 96 | currentRoom.connectionUsed[i+1] += 1 97 | break 98 | 99 | currentLetter = nextLetter 100 | return str(currentLetter) 101 | 102 | if __name__ == '__main__': 103 | NAME = "" 104 | SCHOOL = "" 105 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 106 | while True: 107 | print("\n") 108 | plan, p, q = input().split() 109 | spy = Spy(list(plan), len(plan) + 2) 110 | spy.construct_map() 111 | print(spy.move(int(p)) + spy.move(int(q))) 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /2020/bio20q3.py: -------------------------------------------------------------------------------- 1 | # This is HEAVILY inspired by Matthewelse. His solutions are also available on GitHub! 2 | 3 | from functools import lru_cache 4 | 5 | 6 | # The only way that some plans repeat is when they have the same number of characters remaining, 7 | # and the same last character on the prefix. 8 | @lru_cache(maxsize=None) 9 | def count(remaining_characters, last_char, last_char_count, max_char_count, letters): 10 | if last_char_count is not None and last_char_count > max_char_count: 11 | # impossible 12 | return 0 13 | elif remaining_characters == 0: 14 | return 1 15 | else: 16 | total = 0 17 | 18 | for i in range(letters): 19 | if last_char is not None and last_char == i: 20 | total += count( 21 | remaining_characters - 1, 22 | last_char, 23 | last_char_count + 1, 24 | max_char_count, 25 | letters, 26 | ) 27 | else: 28 | total += count(remaining_characters - 1, i, 1, max_char_count, letters) 29 | 30 | return total 31 | 32 | 33 | NAME = "" 34 | SCHOOL = "" 35 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 36 | 37 | letters, max_adjacent, expected_length = tuple(int(x) for x in input().split(" ")) 38 | 39 | n = int(input()) 40 | prefix = [] 41 | last = None 42 | last_count = 0 43 | 44 | # Find the nth plan's prefix until the nth plan is constructed. 45 | while len(prefix) < expected_length: 46 | for i in range(letters): 47 | # How many of the past letters (in a row) are the same as this one. 48 | _last_count = last_count + 1 if i == last else 1 49 | prefix.append(i) 50 | with_prefix = count( 51 | expected_length - len(prefix), i, _last_count, max_adjacent, letters 52 | ) 53 | 54 | if with_prefix >= n: 55 | # this is the right prefix 56 | last = i 57 | last_count = _last_count 58 | break 59 | 60 | # This wasn't the right prefix. 61 | # So subtract the number of plans starting with this prefix from the number of plans we are searching for. 62 | n -= with_prefix 63 | prefix.pop() # Getting rid of the prefix we temporarily added in this loop. 64 | 65 | # Print the string that is currently stored in an array of ascii numbers. 66 | print("".join(chr(i + ord("A")) for i in prefix)) 67 | -------------------------------------------------------------------------------- /2021/BIO2021q1.py: -------------------------------------------------------------------------------- 1 | from string import ascii_uppercase 2 | from functools import lru_cache 3 | from itertools import permutations 4 | 5 | 6 | # We cache results of the pat() function since we will need to reuse the same strings multiple times. 7 | # We could implement our own memoization, or we can just use lru_cache. 8 | @lru_cache(maxsize=None) 9 | def pat(word): 10 | if len(word) == 1: # A word of length one is definitely a pat. 11 | return True 12 | 13 | # Check all possible splits of the word. 14 | for i in range(len(word) - 1): 15 | left = word[:i + 1] 16 | right = word[i + 1:] 17 | 18 | """ 19 | We want to check if every letter in left is smaller (lexicographically) than every letter in right. 20 | For this problem, it is sufficient to do this in O(n^2): 21 | if all(x > y for y in right for x in left) 22 | 23 | However, we can do this in O(n): 24 | We just need to find whether the smallest letter of left is larger than the largest of right! 25 | """ 26 | 27 | if min(left) > max(right): 28 | if pat(left[::-1]) and pat(right[::-1]): 29 | return True 30 | 31 | return False 32 | 33 | 34 | def solve(): 35 | s1, s2 = input().split() 36 | print("YES" if pat(s1) else "NO") 37 | print("YES" if pat(s2) else "NO") 38 | print("YES" if pat(s1 + s2) else "NO") 39 | 40 | 41 | def b(): 42 | p = ["".join(s) for s in permutations("ABCD")] 43 | for string in p: 44 | if pat(string): 45 | print(string) 46 | 47 | 48 | # A modified version of the pat() function. 49 | # Counts the number of pats in a given word. 50 | @lru_cache(maxsize=None) 51 | def num_pats(word): 52 | if len(word) == 1: 53 | return 1 54 | 55 | ans = 0 56 | # Check all possible splits of the word. 57 | for i in range(len(word) - 1): 58 | left = word[:i + 1] 59 | right = word[i + 1:] 60 | if min(left) > max(right): 61 | if pat(left[::-1]) and pat(right[::-1]): 62 | ans += 1 63 | 64 | return ans 65 | 66 | 67 | def c(): 68 | # We can check the number of pats up to a couple of letters using num_pats and permutations. 69 | # While you could realise that they are simply the Catalan Numbers, 70 | # let's make a recurrence relation to solve this problem without knowledge of them! 71 | # 72 | # Let P[i] be the number of pats using i+1 letters. 73 | # P[n+1] = Sum of P[i]P[n-i] for all i between 0 and n inclusive. 74 | # (i.e. using the first i letters and then the next n-i letters, 75 | # since the right part must use larger letters than the left.) 76 | # 77 | # We can ignore A because, for any pat starting with B made of letters B...Z, 78 | # there is only one place that the A can go such that the resulting word is also a pat. 79 | # Therefore, we want P[23]. 80 | 81 | P = [0 for _ in range(24)] 82 | P[0] = 1 83 | for n in range(23): 84 | for i in range(0, n+1): 85 | P[n+1] += P[i] * P[n-i] 86 | 87 | print(P[23]) # 343059613650 88 | 89 | 90 | if __name__ == '__main__': 91 | NAME = "" 92 | SCHOOL = "" 93 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 94 | solve() 95 | -------------------------------------------------------------------------------- /2021/BIO2021q2.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is an implementation problem, and an extremely finicky one at that! 3 | I recommend really reading the problem carefully and then writing up a high-level plan. 4 | Mine would be: 5 | 1. Get players, moves and respective traversals. 6 | 2. For the number of moves: 7 | 2.1 Traverse a single side. 8 | 2.2 Check if the triangle adjacent to where the player ended up would score the player at least a point. 9 | 2.3 If it will or the player has used up his max. traversals, finish the move, otherwise go back to 2.1. 10 | 2.4 Fill in the triangle adjacent to the player's starting point and check whether it has scored the player a point. 11 | 2.5 Reposition all players if they need to be repositioned. 12 | 3. Print all scores in order. 13 | 4. Traverse from the top-left side until you reach the same side again. 14 | Keep track of the distance travelled to find and print the perimeter. 15 | 16 | One challenge we need to solve is to find a way to store an "infinite" triangular grid. 17 | My way was to use a 2D list as shown below. 18 | [ududu] 19 | [dudud] 20 | [ududu] 21 | where u is a triangle facing upwards and d is facing down. 22 | Since we only have 5000 moves, we don't need a massive 2D array. 23 | """ 24 | import numpy as np 25 | from functools import lru_cache 26 | MAXSIZE = 500 27 | 28 | grid = np.zeros(shape=(MAXSIZE*2+1, MAXSIZE*2+1)) 29 | 30 | grid[MAXSIZE][MAXSIZE] = -1 31 | 32 | """ 1 33 | / \ \-----/ 34 | 1 / \ 2 3 \ / 2 Side numbers 35 | /_____\ \ / 36 | 3 37 | """ 38 | 39 | 40 | # Check the three 'larger triangles' around the cell at a certain row and column 41 | def score(row, column, player_num): 42 | def match(r, c): 43 | return grid[r][c] == player_num 44 | # Notice that in odd index rows, odd columns are facing up and on even rows, even columns are facing up. 45 | if row%2==column%2: # Facing up 46 | return int(match(row-1, column+1) and match(row, column+2)) + \ 47 | int(match(row-1, column-1) and match(row, column-2)) + \ 48 | int(match(row+1, column-1) and match(row+1, column+1)) 49 | 50 | else: # Facing down 51 | return int(match(row+1, column-1) and match(row, column-2)) + \ 52 | int(match(row+1, column+1) and match(row, column+2)) + \ 53 | int(match(row-1, column-1) and match(row-1, column+1)) 54 | 55 | 56 | # dRow, dColumn, new side 57 | up = { 58 | 1: [ 59 | (-1, -1, 3), 60 | (-1, 0, 3), 61 | (-1, 1, 1), 62 | (0, 1, 1), 63 | (0, 0, 2) 64 | ], 65 | 66 | 2: [ 67 | (0, 2, 1), 68 | (1, 2, 1), 69 | (1, 1, 2), 70 | (1, 0, 2), 71 | (0, 0, 3) 72 | ], 73 | 74 | 3: [ 75 | (1, -1, 2), 76 | (1, -2, 2), 77 | (0, -2, 3), 78 | (0, -1, 3), 79 | (0, 0, 1) 80 | ] 81 | } 82 | 83 | # We check almost exactly the same triangles as above. 84 | # So we can copy some bits from above, but this is still q2. It is going to be a bit tedious! 85 | down = { 86 | 1: [ 87 | (-1, 1, 3), 88 | (-1, 2, 1), 89 | (0, 2, 1), 90 | (0, 1, 2), 91 | (0, 0, 2) 92 | ], 93 | 94 | 2: [ 95 | (1, 1, 1), 96 | (1, 0, 2), 97 | (1, -1, 2), 98 | (0, -1, 3), 99 | (0, 0, 3) 100 | ], 101 | 102 | 3: [ 103 | (0, -2, 2), 104 | (-1, -2, 3), 105 | (-1, -1, 3), 106 | (-1, 0, 1), 107 | (0, 0, 1) 108 | ] 109 | } 110 | 111 | 112 | between_up = { 113 | 1: (0, -1), 114 | 2: (0, 1), 115 | 3: (1, 0) 116 | } 117 | 118 | between_down = { 119 | 1: (-1, 0), 120 | 2: (0, 1), 121 | 3: (0, -1) 122 | } 123 | 124 | # Store the top left triangle and starting point globally. 125 | topleft = (500, 500) 126 | topside = 1 127 | 128 | 129 | @lru_cache(maxsize=None) 130 | def adjacent(row, column, side): 131 | if row%2 == column%2: 132 | dRow, dCol = between_up[side] 133 | else: 134 | dRow, dCol = between_down[side] 135 | return row + dRow, column + dCol 136 | 137 | 138 | class Player: 139 | def __init__(self, num, traversals_per_move): 140 | self.row = self.column = MAXSIZE 141 | self.side = 1 142 | self.num = num 143 | self.traversals = traversals_per_move 144 | self.points = 0 145 | 146 | def traverse(self): 147 | for i in range(5): 148 | if self.row % 2 == self.column % 2: 149 | dRow, dColumn, new = up[self.side][i] 150 | else: 151 | dRow, dColumn, new = down[self.side][i] 152 | 153 | if grid[self.row + dRow][self.column + dColumn]: 154 | self.row += dRow 155 | self.column += dColumn 156 | self.side = new 157 | break 158 | 159 | def move(self): 160 | global topleft, topside 161 | # Debugging is especially essential for q2. 162 | # print(f"Player {self.num}") 163 | # Store the position of the triangle adjacent to the player. 164 | start_row, start_col = adjacent(self.row, self.column, self.side) 165 | 166 | for i in range(self.traversals): 167 | self.traverse() 168 | temp_row, temp_col = adjacent(self.row, self.column, self.side) 169 | if score(temp_row, temp_col, self.num): 170 | break 171 | 172 | # Fill in the start point, and check whether that new triangle is the top left one. 173 | grid[start_row][start_col] = self.num 174 | if start_row < topleft[0] or start_row == topleft[0] and start_col < topleft[1]: 175 | topleft = (start_row, start_col) 176 | topside = 1 if start_row%2==start_col%2 else 3 177 | 178 | # Check if the player gets any points this turn. 179 | self.points += score(start_row, start_col, self.num) 180 | # print(self.row, self.column, self.side) 181 | # print("\n".join(str(x[496:505]).replace(" ", "") for x in grid[496:505])) 182 | # print("\n") 183 | 184 | def reposition(self): 185 | adjRow, adjCol = adjacent(self.row, self.column, self.side) 186 | 187 | if grid[adjRow][adjCol]: 188 | self.row, self.column = topleft 189 | self.side = topside 190 | 191 | 192 | def find_perimeter(): 193 | temp = Player(-1, 0) 194 | temp.row, temp.column = topleft 195 | temp.side = topside 196 | perimeter = 0 197 | while True: 198 | temp.traverse() 199 | perimeter += 1 200 | if (temp.row, temp.column) == topleft and temp.side == topside: 201 | print(perimeter) 202 | break 203 | 204 | 205 | def solve(): 206 | p, moves = (int(x) for x in input().split()) 207 | players = [] 208 | traversals = [int(x) for x in input().split()] 209 | for i in range(p): 210 | players.append(Player(i+1, traversals[i])) 211 | 212 | while moves != 0: 213 | for i in range(p): 214 | players[i].move() 215 | for j in range(p): 216 | players[j].reposition() 217 | moves -= 1 218 | if moves == 0: 219 | break 220 | 221 | for i in range(p): 222 | print(players[i].points) 223 | 224 | find_perimeter() 225 | 226 | 227 | def b(): 228 | # Just do this by hand! Free marks :) 229 | return 230 | 231 | 232 | def c(): 233 | global grid, topleft, topside, MAXSIZE 234 | MAXSIZE = 6 235 | 236 | def reset(): 237 | global grid, topleft, topside 238 | grid = np.zeros(shape=(MAXSIZE * 2 + 1, MAXSIZE * 2 + 1)) 239 | 240 | grid[MAXSIZE][MAXSIZE] = -1 241 | topleft = (MAXSIZE, MAXSIZE) 242 | topside = 1 243 | m = 4 244 | wanted = np.zeros(shape=(MAXSIZE * 2 + 1, MAXSIZE * 2 + 1)) 245 | wanted[MAXSIZE][MAXSIZE] = -1 246 | wanted[MAXSIZE][MAXSIZE-2] = 1 247 | wanted[MAXSIZE][MAXSIZE-1] = 1 248 | wanted[MAXSIZE+1][MAXSIZE-2] = 1 249 | wanted[MAXSIZE+1][MAXSIZE] = 1 250 | 251 | # Realistically, we only need to check between 1 and 100 (the scope of the original problem) 252 | for i in range(1, 101): 253 | reset() 254 | player = Player(1, i) 255 | for _ in range(m): 256 | player.move() 257 | player.reposition() 258 | 259 | # See if the grid we created is the exact same as the grid we wanted. 260 | finished = True 261 | for j in range(MAXSIZE*2+1): 262 | for k in range(MAXSIZE*2+1): 263 | if grid[j][k] != wanted[j][k]: 264 | finished = False 265 | if finished: 266 | print(i) # 51 267 | 268 | 269 | def d(): 270 | # Solve with 2 5000\n 5 7 to fill the grid initially. 271 | unfilled = 0 272 | seen = set() 273 | from collections import deque 274 | queue = deque() 275 | 276 | # Fill in the triangles outside the perimeter. 277 | def fill(row, column): 278 | queue.append((row, column)) 279 | 280 | while queue: 281 | r, c = queue.popleft() 282 | grid[r][c] = -1 283 | up = r%2==c%2 284 | if r: 285 | if not up and (r-1, c) not in seen and not grid[r-1, c]: 286 | seen.add((r-1, c)) 287 | queue.append((r-1, c)) 288 | if c: 289 | if (r, c-1) not in seen and not grid[r, c-1]: 290 | seen.add((r, c-1)) 291 | queue.append((r, c-1)) 292 | if r < 1000: 293 | if up and (r+1, c) not in seen and not grid[r+1, c]: 294 | seen.add((r+1, c)) 295 | queue.append((r+1, c)) 296 | if c < 1000: 297 | if (r, c+1) not in seen and not grid[r, c+1]: 298 | seen.add((r, c+1)) 299 | queue.append((r, c+1)) 300 | 301 | fill(0, 0) 302 | # Find the triangles still not filled. 303 | for i in range(1001): 304 | for j in range(1001): 305 | if not grid[i][j]: 306 | unfilled += 1 307 | print(unfilled) # 377 308 | 309 | 310 | if __name__ == '__main__': 311 | NAME = "" 312 | SCHOOL = "" 313 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 314 | solve() 315 | # c() 316 | # d() 317 | -------------------------------------------------------------------------------- /2021/BIO2021q3.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from itertools import permutations 3 | warehouse = ["A", "B", "C", "D", "E", "F", "G", "H"] 4 | 5 | 6 | # Implement the three operations in the question. 7 | def add(cur): 8 | cur += warehouse[len(cur)] 9 | return cur 10 | 11 | 12 | def swap(cur): 13 | return cur[:2][::-1] + cur[2:] 14 | 15 | 16 | def rotate(cur): 17 | return cur[1:] + cur[0] 18 | 19 | 20 | def bfs(start, target): 21 | steps = {start: 0} # Stores number of steps required to get to a state. 22 | queue = deque() 23 | queue.append(start) 24 | target_len = len(target) 25 | cur = "" 26 | res = None 27 | 28 | def on_reached(state): 29 | # We only care if the new value hasn't been reached. 30 | # If it has been reached earlier, it was reached in the same or fewer steps. 31 | if state not in steps: 32 | steps[state] = steps[cur] + 1 33 | if state == target: 34 | return steps[state] 35 | queue.append(state) 36 | 37 | while queue: 38 | cur = queue.popleft() 39 | len_cur = len(cur) 40 | 41 | if len_cur < target_len: # If there are boxes left at the warehouse 42 | new = add(cur) 43 | # Call the on_reached() function and return the result if it exists. 44 | res = on_reached(new) 45 | if res: 46 | return res 47 | 48 | if len_cur > 1: # If at least two boxes 49 | new = swap(cur) 50 | res = on_reached(new) 51 | if res: 52 | return res 53 | 54 | if len_cur: # If at least one box 55 | new = rotate(cur) 56 | res = on_reached(new) 57 | if res: 58 | return res 59 | 60 | 61 | def solve(): 62 | global warehouse 63 | target = input() 64 | start = "" 65 | warehouse = warehouse[:len(target)] 66 | print(bfs(start, target)) # Find the minimum steps to get from the start state to the target state. 67 | 68 | 69 | def b(): 70 | global warehouse 71 | p = ["".join(s) for s in permutations("ABCDE")] 72 | warehouse = warehouse[:5] 73 | 74 | for string in p: 75 | min_ops = bfs("", string) 76 | if min_ops == 6: 77 | print(string) 78 | 79 | 80 | def modified_bfs(start, target, length): 81 | # Instead of storing the minimum steps to reach a state, 82 | # let's store the ways to make a state in a certain number of steps. 83 | # e.g. we can make the starting value in 0 steps in exactly 1 way! 84 | ways = {(start, 0): 1} 85 | queue = deque() 86 | queue.append((start, 0)) 87 | target_len = len(target) 88 | 89 | while queue: 90 | cur, steps = queue.popleft() 91 | 92 | if steps == length: 93 | print(ways[(target, length)]) # 84 94 | exit(0) 95 | 96 | len_cur = len(cur) 97 | 98 | if len_cur < target_len: 99 | new = add(cur) 100 | if (new, steps+1) not in ways: 101 | ways[(new, steps+1)] = ways[(cur, steps)] 102 | queue.append((new, steps+1)) 103 | 104 | else: 105 | ways[(new, steps+1)] += ways[(cur, steps)] 106 | 107 | if len_cur > 1: # If at least two boxes 108 | new = swap(cur) 109 | if (new, steps + 1) not in ways: 110 | ways[(new, steps + 1)] = ways[(cur, steps)] 111 | queue.append((new, steps + 1)) 112 | 113 | else: 114 | ways[(new, steps + 1)] += ways[(cur, steps)] 115 | 116 | if len_cur: # If at least one box 117 | new = rotate(cur) 118 | if (new, steps + 1) not in ways: 119 | ways[(new, steps + 1)] = ways[(cur, steps)] 120 | queue.append((new, steps + 1)) 121 | 122 | else: 123 | ways[(new, steps + 1)] += ways[(cur, steps)] 124 | 125 | 126 | def c(): 127 | modified_bfs("", "HGFEDCBA", 24) 128 | 129 | 130 | if __name__ == '__main__': 131 | NAME = "" 132 | SCHOOL = "" 133 | print(f"Name: {NAME}\nSchool: {SCHOOL}") 134 | solve() 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # British-Informatics-Olympiad 2 | 3 | 4 | Very readable, relatively easy-to-understand solutions for the British Informatics Olympiad. 5 | While most of these solutions are done without any help, a few of these files are modified versions of code by **Matthewelse** and **Spookiel**. I definitely recommend checking them out. 6 | 7 | Please don't hesitate to send any improvements, though I will only make these changes as long as the code remains readable. 8 | 9 | Disclaimer: These are by no means official solutions and are written solely in Python. Not all solutions are complete or give full marks, but the most recent years should be more complete. 10 | 11 | These things said, I hope these are helpful to programmers practicing for this and similar competitions. 12 | 13 | **Tips:** 14 | 15 | 1. (Optional) Get Pycharm! I used to use the standard IDLE, but Pycharm has helped me improve my efficiency significantly, especially for longer pieces of code. 16 | 2. Learn the basics - Python syntax (or you might use another language), recursion, BFS, DFS and memoization are all pretty much essential for the BIO. The BIO doesn't really stretch far beyond these concepts, though. If you want more diverse problems, I recommend CSES and Codeforces. If you want more maths, go for Project Euler! 17 | 3. Try the problems first on your own, whether it takes 10 minutes, an hour or 3 hours! The problems from earlier years are definitely easier, so perhaps start with those. 18 | 4. Once you have, find how you scored. Did you end up with a logical error? Or did you get a TLE? Or was it perfect? 19 | 5. Whatever your outcome, check these solutions; the comments and the variable names should help you get a better understanding of the problem. 20 | 6. When testing, being able to input multiple test cases is often handy. I don't recommend going as far as unit testing, but try using a while loop. 21 | 7. Then leave it for a while and try doing the problem on your own again! 22 | 8. I sometimes add comments in the extra description, so do check them out. (You can access this when you are looking at a solution by pressing the ... next to the description at the top). 23 | 24 | -------------------------------------------------------------------------------- /docs/googled40a215ed9e79ad6.html: -------------------------------------------------------------------------------- 1 | google-site-verification: googled40a215ed9e79ad6.html -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | British Informatics Olympiad Solutions (in Python) 4 | 5 | 6 | 7 | 8 |

9 | British Informatics Olympiad Solutions (in Python) 10 |

11 | 12 |

13 | 14 | Visit My Github page! 15 |

16 | Very readable, relatively easy-to-understand solutions for the British Informatics Olympiad. 17 | Please do check it out! 18 |

19 | 20 | --------------------------------------------------------------------------------