├── 1996 ├── bio96-exam.pdf ├── bio96-marks.pdf ├── 1996 Q1 - Amicable Numbers.py ├── 1996 Q2 - Life.py └── 1996 Q3 - Dominoes.py ├── 1997 ├── bio97-exam.pdf ├── bio97-marks.pdf ├── 1997 Q1 - Time.py ├── 1997 - Egyptian Fractions - FULL MARKS.py └── 1997 Q3 - Eqyptian Fractions.py ├── 1998 ├── bio98-exam.pdf ├── bio98-marks.pdf ├── 1998 Q1 - Roman Numerals.py ├── 1998 Q2 - Tamworth Two.py └── 1998 Q3 - Alphametics (Cryptarithms).py ├── 1999 ├── bio99-exam.pdf ├── bio99-marks.pdf ├── 1999 Q1 - Digital Rivers.py ├── 1999 Q3 - Playing Games.py └── 1999 Q2 - Black Box (Atom).py ├── 2000 ├── bio00-exam.pdf ├── bio00-marks.pdf ├── 2000 Q1 - Passwords.py ├── 2000 Q2 - Ants.py └── 2000 Q3 - Dice Games.py ├── 2001 ├── bio01-exam.pdf ├── bio01-marks.pdf ├── 2001 Q1 - Eenie, Meenie, Mainie, Mo!.py ├── 2001 Q3 - A Space Oddity.py └── 2001 Q2 - Playfair Cipher.py ├── 2002 ├── bio02-exam.pdf ├── bio02-marks.pdf ├── 2002 Q1 - Lojban.py └── 2002 Q2 - Shuffling.py ├── 2003 ├── bio03-exam.pdf ├── bio03-marks.pdf ├── 2003 Q1 - ISBN.py ├── 2003 Q3 - New Order.py └── 2003 Q2 - Waves - Isaac Eason.py ├── 2004 ├── bio04-exam.pdf ├── bio04-marks.pdf ├── 2004 Q1 - Mayan Calendar.py ├── 2004 Q3 - Morse Code.py └── 2004 Q2 - Four in a Line.py ├── 2005 ├── bio05-exam.pdf ├── bio05-marks.pdf ├── 2005 Q2 - Turing Machine.py ├── 2005 Q3 - Movie Magic.py └── 2005 Q1 - Fractions.py ├── 2006 ├── bio06-exam.pdf ├── bio06-marks.pdf ├── 2006 Q1 - Anagrams.py ├── 2006 Q3 - Drats.py └── 2006 Q2 - Rules.py ├── 2007 ├── bio07-exam.pdf ├── bio07-marks.pdf ├── 2007 Q1 - Cards.py ├── 2007 Q3 - String Rewriting.py └── 2007 Q2 - Mu Torere.py ├── 2008 ├── bio08-exam.pdf ├── bio08-marks.pdf ├── 2008 Q1 - Goldbach Conjecture.py ├── 2008 Q2 - Enigma Machine.py └── 2008 Q3 - Shirts.py ├── 2009 ├── bio09-exam.pdf ├── bio09-marks.pdf ├── 2009 Q1 - Digit Words.py ├── 2009 Q3 - Child's Play.py └── 2009 Q2 - Puzzle Game.py ├── 2010 ├── bio10-exam.pdf ├── bio10-marks.pdf ├── 2010 Q1 - Anagram Numbers.py ├── 2010 Q3 - Juggl(ug)ling.py └── 2010 Q2 - Die Tipping.py ├── 2011 ├── bio11-exam.pdf ├── bio11-marks.pdf ├── 2011 Q1 - Fibonacci Letters.py ├── 2011 Q3 - Upside-Down.py └── 2011 Q2 - Acordion Patience.py ├── 2012 ├── bio12-exam.pdf ├── bio12-marks.pdf ├── 2012 Q2 - On the Right Track.py └── 2012 Q3 - Number Ladder.py ├── 2013 ├── bio13-exam.pdf ├── bio13-marks.pdf ├── 2013 Q1 - Watching the Clock.py ├── 2013 Q2 - Neutron.py └── 2013 Q3 - Unlock.py ├── 2014 ├── bio14-exam.pdf ├── bio14-marks.pdf ├── 2014 Q3 - Increasing Passwords.py └── 2014 Q2 - Loops.py ├── 2015 ├── bio15-exam.pdf ├── bio15-marks.pdf ├── 2015 Q1 - Block Palindromes.py ├── 2015 Q2 - Battleships.py └── 2015 Q3 - Modern Art.py ├── 2016 ├── bio16-exam.pdf ├── bio16-marks.pdf ├── 2016 Q1 - Promenade.py ├── 2016 Q2 - Migration.py └── 2016 Q3 - Prime Connections.py ├── 2017 ├── bio17-exam.pdf ├── bio17-marks.pdf ├── 2017 Q3 - Mystery Parcels.py ├── 2017 Q1 - Coloured Triangles.py └── 2017 Q2 - Dots and Boxes.py ├── 2018 ├── bio18-exam.pdf ├── bio18-marks.pdf ├── 2018 Q1 - Debt Repayment.py ├── 2018 Q2 - Decoder Ring.py └── 2018 Q3 - Serial Numbers.py ├── 2019 ├── bio19-exam.pdf ├── bio19-marks.pdf ├── 2019 Q2 - Trail.py ├── 2019 Q1 - Palindromic Numbers.py └── 2019 Q3 - Block-Chain.py ├── 2020 ├── bio20-exam.pdf ├── bio20-marks.pdf ├── 2020 Q1 - Roman Look-and-Say.py ├── 2020 Q3 - False Plan.py └── 2020 Q2 - Alpha Complex.py ├── 2021 ├── bio21-exam.pdf ├── bio21-marks.pdf ├── 2021 Q1 - Down Pat.py └── 2021 Q3 - Window Dressing.py ├── 2022 ├── bio22-exam.pdf ├── bio22-marks.pdf ├── 2022 Q1 - Decrypt.py ├── 2022 Q3 - Parking.py └── 2022 Q2 - Game of Drones.py ├── 2023 ├── bio23-exam.pdf ├── 2023 - Q1 Zeckendorf Representation.py └── 2023 - Q3 Dreaming Spires.py ├── 2024 ├── bio24-exam.pdf ├── bio24-marks.pdf ├── 2024 - Q1 Integer Strings.py ├── 2024 - Q3 Word Game.py └── 2024 - Q2 Parsing Lists.py ├── 2025 ├── 2025 Q1 - Palidromic Sums.py └── 2025 Q3 - Fuses.py └── README.md /1996/bio96-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/1996/bio96-exam.pdf -------------------------------------------------------------------------------- /1997/bio97-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/1997/bio97-exam.pdf -------------------------------------------------------------------------------- /1998/bio98-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/1998/bio98-exam.pdf -------------------------------------------------------------------------------- /1999/bio99-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/1999/bio99-exam.pdf -------------------------------------------------------------------------------- /2000/bio00-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2000/bio00-exam.pdf -------------------------------------------------------------------------------- /2001/bio01-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2001/bio01-exam.pdf -------------------------------------------------------------------------------- /2002/bio02-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2002/bio02-exam.pdf -------------------------------------------------------------------------------- /2003/bio03-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2003/bio03-exam.pdf -------------------------------------------------------------------------------- /2004/bio04-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2004/bio04-exam.pdf -------------------------------------------------------------------------------- /2005/bio05-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2005/bio05-exam.pdf -------------------------------------------------------------------------------- /2006/bio06-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2006/bio06-exam.pdf -------------------------------------------------------------------------------- /2007/bio07-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2007/bio07-exam.pdf -------------------------------------------------------------------------------- /2008/bio08-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2008/bio08-exam.pdf -------------------------------------------------------------------------------- /2009/bio09-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2009/bio09-exam.pdf -------------------------------------------------------------------------------- /2010/bio10-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2010/bio10-exam.pdf -------------------------------------------------------------------------------- /2011/bio11-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2011/bio11-exam.pdf -------------------------------------------------------------------------------- /2012/bio12-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2012/bio12-exam.pdf -------------------------------------------------------------------------------- /2013/bio13-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2013/bio13-exam.pdf -------------------------------------------------------------------------------- /2014/bio14-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2014/bio14-exam.pdf -------------------------------------------------------------------------------- /2015/bio15-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2015/bio15-exam.pdf -------------------------------------------------------------------------------- /2016/bio16-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2016/bio16-exam.pdf -------------------------------------------------------------------------------- /2017/bio17-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2017/bio17-exam.pdf -------------------------------------------------------------------------------- /2018/bio18-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2018/bio18-exam.pdf -------------------------------------------------------------------------------- /2019/bio19-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2019/bio19-exam.pdf -------------------------------------------------------------------------------- /2020/bio20-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2020/bio20-exam.pdf -------------------------------------------------------------------------------- /2021/bio21-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2021/bio21-exam.pdf -------------------------------------------------------------------------------- /2022/bio22-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2022/bio22-exam.pdf -------------------------------------------------------------------------------- /2023/bio23-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2023/bio23-exam.pdf -------------------------------------------------------------------------------- /2024/bio24-exam.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2024/bio24-exam.pdf -------------------------------------------------------------------------------- /1996/bio96-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/1996/bio96-marks.pdf -------------------------------------------------------------------------------- /1997/bio97-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/1997/bio97-marks.pdf -------------------------------------------------------------------------------- /1998/bio98-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/1998/bio98-marks.pdf -------------------------------------------------------------------------------- /1999/bio99-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/1999/bio99-marks.pdf -------------------------------------------------------------------------------- /2000/bio00-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2000/bio00-marks.pdf -------------------------------------------------------------------------------- /2001/bio01-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2001/bio01-marks.pdf -------------------------------------------------------------------------------- /2002/bio02-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2002/bio02-marks.pdf -------------------------------------------------------------------------------- /2003/bio03-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2003/bio03-marks.pdf -------------------------------------------------------------------------------- /2004/bio04-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2004/bio04-marks.pdf -------------------------------------------------------------------------------- /2005/bio05-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2005/bio05-marks.pdf -------------------------------------------------------------------------------- /2006/bio06-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2006/bio06-marks.pdf -------------------------------------------------------------------------------- /2007/bio07-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2007/bio07-marks.pdf -------------------------------------------------------------------------------- /2008/bio08-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2008/bio08-marks.pdf -------------------------------------------------------------------------------- /2009/bio09-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2009/bio09-marks.pdf -------------------------------------------------------------------------------- /2010/bio10-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2010/bio10-marks.pdf -------------------------------------------------------------------------------- /2011/bio11-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2011/bio11-marks.pdf -------------------------------------------------------------------------------- /2012/bio12-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2012/bio12-marks.pdf -------------------------------------------------------------------------------- /2013/bio13-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2013/bio13-marks.pdf -------------------------------------------------------------------------------- /2014/bio14-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2014/bio14-marks.pdf -------------------------------------------------------------------------------- /2015/bio15-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2015/bio15-marks.pdf -------------------------------------------------------------------------------- /2016/bio16-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2016/bio16-marks.pdf -------------------------------------------------------------------------------- /2017/bio17-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2017/bio17-marks.pdf -------------------------------------------------------------------------------- /2018/bio18-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2018/bio18-marks.pdf -------------------------------------------------------------------------------- /2019/bio19-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2019/bio19-marks.pdf -------------------------------------------------------------------------------- /2020/bio20-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2020/bio20-marks.pdf -------------------------------------------------------------------------------- /2021/bio21-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2021/bio21-marks.pdf -------------------------------------------------------------------------------- /2022/bio22-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2022/bio22-marks.pdf -------------------------------------------------------------------------------- /2024/bio24-marks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmncollins/BIO-Solutions/HEAD/2024/bio24-marks.pdf -------------------------------------------------------------------------------- /2006/2006 Q1 - Anagrams.py: -------------------------------------------------------------------------------- 1 | a = sorted(list(input())) 2 | b = sorted(list(input())) 3 | 4 | if a == b: 5 | print("Anagrams") 6 | else: 7 | print("Not anagrams") 8 | 9 | """ 10 | b) 6 11 | c) 21 12 | AAAAA 13 | AAAAB 14 | AAAAC 15 | AAABB 16 | AAACC 17 | AAABC 18 | AABBB 19 | AACCC 20 | AABCC 21 | AABBC 22 | ABBBB 23 | ACCCC 24 | ABBBC 25 | ABBCC 26 | ABCCC 27 | BBBBB 28 | CCCCC 29 | BBBBC 30 | BBBCC 31 | BBCCC 32 | BCCCC 33 | """ -------------------------------------------------------------------------------- /2000/2000 Q1 - Passwords.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | word = input() 4 | 5 | def valid(word): 6 | for i in range(len(word)-1): 7 | for j in range(i+1, len(word)): 8 | # print(word[i:j]) 9 | if len(re.findall(r"(" + word[i:j] + word[i:j] + r")+", word)) > 0: return False 10 | return True 11 | 12 | if valid(word): 13 | print("Accepted") 14 | else: 15 | print("Rejected") 16 | 17 | """ 18 | b) 7 letters: ABACABA 19 | """ -------------------------------------------------------------------------------- /2011/2011 Q1 - Fibonacci Letters.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | a, b, c = input().split() 4 | c = int(c) 5 | start = time.time() 6 | 7 | if c == 1: print(a) 8 | if c == 2: print(b) 9 | 10 | alpha = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 11 | a = alpha.index(a) + 1 12 | b = alpha.index(b) + 1 13 | 14 | for i in range(max(0, c-2)): 15 | A = (a+b) % 26 16 | if A == 0: A = 26 17 | a = b 18 | b = A 19 | # print(A) 20 | # print(alpha[b-1]) 21 | 22 | if c > 2: 23 | print(alpha[b-1]) 24 | 25 | print("Time:", time.time() - start) 26 | 27 | """ 28 | b) F + R = X 29 | Q + Q = H 30 | 31 | c) 32 | """ -------------------------------------------------------------------------------- /2016/2016 Q1 - Promenade.py: -------------------------------------------------------------------------------- 1 | 2 | l, m, r, s = 1, 0, 0, 1 3 | 4 | q = input() 5 | 6 | curr = (l + r, m + s) 7 | for item in q: 8 | if item == "L": 9 | l = curr[0] 10 | m = curr[1] 11 | else: 12 | r = curr[0] 13 | s = curr[1] 14 | curr = (l + r, m + s) 15 | print(curr[0], "/", curr[1]) 16 | 17 | """ 18 | b) 3/5 + 1/5 = 4/5 = LRRR 19 | c) 0 Rs, 999,999 Ls 20 | d) No. A negative fraction needs a negative numerator or denominator. The sum of positive numbers is always positive. 21 | The promenades always add positive numbers so a negative numerator or denominator is never produced 22 | """ 23 | -------------------------------------------------------------------------------- /2010/2010 Q1 - Anagram Numbers.py: -------------------------------------------------------------------------------- 1 | num = int(input()) 2 | 3 | 4 | def anagram(num): 5 | numbers = [] 6 | for i in range(2,10): 7 | q = str(num * i) 8 | if sorted(list(q)) == sorted(list(str(num))): 9 | numbers.append(i) 10 | return numbers 11 | 12 | numbers = anagram(num) 13 | if len(numbers) == 0: 14 | print("NO") 15 | else: 16 | for n in numbers: print(n, end=" ") 17 | print() 18 | 19 | """ 20 | b) 28415970 [3], 17049582 [5], 14207985 [6] 21 | c) 138 22 | """ 23 | """ 24 | # code for c 25 | tot = 0 26 | for i in range(100000, 1000000): 27 | if i % 10000 == 0 : print(i) 28 | if len(anagram(i)) > 0: 29 | if len(str(i)) == len(set(list(str(i)))): 30 | tot += 1 31 | print("tot:", tot) 32 | """ 33 | -------------------------------------------------------------------------------- /2002/2002 Q1 - Lojban.py: -------------------------------------------------------------------------------- 1 | number = input() 2 | 3 | convert = {"pa": "1", "vo":"4", "ze":"7", "re":"2", "mu":"5", "bi":"8", "no":"0", "ci":"3", "xa":"6", "so":"9"} 4 | reverse = {"1":"pa","2":"re","3":"ci","4":"vo","5":"mu","6":"xa","7":"ze","8":"bi","9":"so","0":"no"} 5 | 6 | ans = "" 7 | for i in range(0, len(number), 2): 8 | ans += convert[number[i:i+2]] 9 | 10 | print(ans) 11 | 12 | # part c 13 | """ 14 | alpha = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ".lower()) 15 | for i in range(1, 10000): 16 | I = str(i) 17 | n = "" 18 | for q in I: n += reverse[q] 19 | tot = 0 20 | for l in n: 21 | tot += alpha.index(l) + 1 22 | if tot == i: 23 | print(n) 24 | """ 25 | 26 | 27 | """ 28 | b) 94 + 26 = 120 = pareno 29 | c) vobi, mupa 30 | 31 | 32 | """ -------------------------------------------------------------------------------- /1996/1996 Q1 - Amicable Numbers.py: -------------------------------------------------------------------------------- 1 | a = int(input()) 2 | b = int(input()) 3 | 4 | def factors(n): 5 | f = set() 6 | for i in range(1,n//2+1): 7 | if n % i == 0: 8 | f.add(i) 9 | f.add(n//i) 10 | f.remove(n) 11 | return f 12 | 13 | def amicable(a,b): 14 | A = sum(factors(a)) 15 | B = sum(factors(b)) 16 | if a == B and b == A: 17 | return True 18 | else: 19 | return False 20 | 21 | if a != b and amicable(a,b): 22 | print("Amicable") 23 | else: 24 | print("Not amicable") 25 | 26 | """ 27 | b) 220 284 28 | """ 29 | 30 | def b(): 31 | for a in range(3,999): 32 | for b in range(3,999): 33 | if a != b: 34 | if amicable(a,b): 35 | print(a,b) 36 | #b() 37 | -------------------------------------------------------------------------------- /2001/2001 Q1 - Eenie, Meenie, Mainie, Mo!.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | n = int(input()) 3 | w = int(input()) 4 | 5 | circle = deque([i+1 for i in range(n)]) 6 | 7 | def game(circle, w, clock=True): 8 | if not clock: circle.rotate(-1) 9 | while len(circle) > 1: 10 | if clock: 11 | circle.rotate(-w) 12 | q = circle.pop() 13 | else: 14 | circle.rotate((w-1)) 15 | q = circle.pop() 16 | # print(q) 17 | return circle[0] 18 | print(game(deque(circle), w)) 19 | 20 | # part c 21 | """ 22 | for i in range(1,1000): 23 | circle = deque([i + 1 for i in range(100)]) 24 | circle2 = deque([i + 1 for i in range(100)]) 25 | 26 | if game(deque(circle), i) == game(deque(circle2), i, False): 27 | print(i) 28 | """ 29 | 30 | 31 | 32 | """ 33 | b) 5, 10, 3, 9, 4, 12, 8, 7, 11, 2, 6, 1 34 | c) 64 words 35 | 36 | """ 37 | -------------------------------------------------------------------------------- /2013/2013 Q1 - Watching the Clock.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | import time 3 | 4 | 5 | a,b = list(map(int, input().split())) 6 | t1 = [0,0] 7 | t2 = [0,0] 8 | 9 | start = time.time() 10 | 11 | #@lru_cache(maxsize=None) 12 | def incrementTime(t, n): 13 | t[1] += n 14 | t[0] = (t[0] + t[1] // 60) % 24 15 | t[1] = t[1] % 60 16 | return t 17 | 18 | def printTime(t): 19 | h = str(t[0]) 20 | m = str(t[1]) 21 | if len(h) != 2: h = "0" + h 22 | if len(m) != 2: m = "0" + m 23 | print(h + ":" + m) 24 | 25 | 26 | t1 = incrementTime(t1, 60 + a) 27 | t2 = incrementTime(t2, 60 + b) 28 | while t1 != t2: 29 | t1 = incrementTime(t1, 60 + a) 30 | t2 = incrementTime(t2, 60 + b) 31 | 32 | printTime(t1) 33 | print("Time:", time.time() - start) 34 | 35 | """ 36 | b) 8 (12:00), 9 (16:00), 16 (18:00), 18 (08:00), 0 (01:00) 37 | 38 | c) greatest difference = 24 * 60 = 1440 so 1440 hours 39 | 40 | """ 41 | -------------------------------------------------------------------------------- /2007/2007 Q1 - Cards.py: -------------------------------------------------------------------------------- 1 | from itertools import combinations 2 | 3 | cards = list(map(int, input().split())) 4 | 5 | sets = [] 6 | for i in range(2,5): 7 | sets += list(combinations(cards, i)) 8 | 9 | tot = 0 10 | for item in sets: 11 | if sum(list(item)) == 15: tot += 1 12 | if len(item) == 2 and len(set(item)) == 1: tot += 1 13 | 14 | print(tot) 15 | 16 | """ 17 | b) 10 9 8 1 2 18 | c) 29. why is it not 29????? 19 | """ 20 | 21 | """ 22 | code for c: 23 | 24 | seen = [] 25 | tot = 0 26 | for a in range(1,11): 27 | for b in range(1,11): 28 | for c in range(1,11): 29 | for d in range(1, 11): 30 | for e in range(1, 11): 31 | l = [a,b,c,d,e] 32 | if sum(l) == 15: 33 | if sorted(l) not in seen: 34 | seen.append(sorted(l)) 35 | print(l) 36 | tot += 1 37 | print(tot) 38 | 39 | """ -------------------------------------------------------------------------------- /1998/1998 Q1 - Roman Numerals.py: -------------------------------------------------------------------------------- 1 | def to_numeral(n): 2 | numerals = {1:"I", 5:"V", 10:"X", 50:"L", 100:"C", 500:"D", 1000:"M"} 3 | num = "" 4 | while n > 0: 5 | for item in sorted(numerals.keys())[::-1]: 6 | if n >= item: 7 | num += numerals[item] 8 | n -= item 9 | break 10 | return num.replace("DCCCC", "CM").replace("CCCC", "CD").replace("LXXXX", "XC").replace("XXXX", "XL").replace("VIIII", "IX").replace("IIII", "IV") 11 | 12 | print(to_numeral(int(input()))) 13 | 14 | """ 15 | b) LXIII + XXXVIII = CI 16 | MCCXLIX + DCXV = MDCCCLXIV 17 | 18 | c) 3800 more 19 | 55 less 20 | """ 21 | 22 | def c(n): 23 | more = 0 24 | less = 0 25 | for i in range(1, n): 26 | numeral = to_numeral(i) 27 | if len(numeral) > len(str(i)): 28 | more += 1 29 | elif len(numeral) < len(str(i)): 30 | less += 1 31 | print(more, "more") 32 | print(less, "less") 33 | #c(4000) 34 | -------------------------------------------------------------------------------- /2009/2009 Q1 - Digit Words.py: -------------------------------------------------------------------------------- 1 | m = input() 2 | 3 | digits = ["ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"] 4 | print(len(m)) 5 | for d in digits: 6 | a = 0 7 | i = -1 8 | found = True 9 | n = list(m) 10 | while a < len(d): 11 | found = False 12 | q = i 13 | while d[a] in n: 14 | q = n.index(d[a]) 15 | n.pop(q) 16 | if q > i: 17 | found = True 18 | break 19 | i -= 1 20 | i = q - 1 21 | a += 1 22 | if not found: 23 | break 24 | if found: 25 | print(digits.index(d) + 1) 26 | break 27 | if not found: 28 | print("NO") 29 | 30 | """ 31 | b) 10 32 | T[1]W[1]O[1] 33 | T[1]W[1]O[2] 34 | T[1]W[1]O[3] 35 | T[1]W[2]O[2] 36 | T[1]W[2]O[3] 37 | T[1]W[3]O[3] 38 | T[2]W[2]O[2] 39 | T[2]W[2]O[3] 40 | T[2]W[3]O[3] 41 | T[3]W[3]O[3] 42 | 43 | c) 12 (THWFIOURVNEE) 44 | 18 (NSFEIGHTHWOURVENEX) 45 | 46 | d) 47 | """ 48 | -------------------------------------------------------------------------------- /2017/2017 Q3 - Mystery Parcels.py: -------------------------------------------------------------------------------- 1 | import time 2 | import functools 3 | from itertools import combinations_with_replacement as combs 4 | from functools import lru_cache 5 | 6 | p, i, n, w = list(map(int, input().split())) 7 | 8 | start = time.time() 9 | 10 | @lru_cache(maxsize=None) 11 | def perm_weight(weight, items, maxWeight): 12 | if weight == 0 and items == 0: 13 | return 1 14 | elif weight <= 0 or items <= 0: 15 | return 0 16 | return sum([perm_weight(weight - i, items - 1) for i in range(1, maxWeight+1)]) 17 | 18 | def perm_parcels(parcels, maxWeight, items, weight): 19 | if parcels == 0 and items == 0: 20 | return 1 21 | elif parcels <= 0 and items <= 0: 22 | return 0 23 | return sum([perm_parcels(parcels - 1, maxWeight, items - i, weight) * perm_weight(weight, i, maxWeight) for i in range(1, items + 1)]) 24 | 25 | print(perm_parcels(p,i,n,w)) 26 | 27 | 28 | 29 | print("Time:", time.time() - start) 30 | 31 | """ 32 | b) 33 | 34 | """ 35 | 36 | 37 | -------------------------------------------------------------------------------- /2008/2008 Q1 - Goldbach Conjecture.py: -------------------------------------------------------------------------------- 1 | import time 2 | import math 3 | 4 | primes = [2] 5 | 6 | for i in range(3,10000): 7 | prime = True 8 | for p in primes: 9 | if p > i**0.5: break 10 | if i % p == 0: 11 | prime = False 12 | break 13 | if prime: 14 | primes.append(i) 15 | 16 | n = int(input()) 17 | start = time.time() 18 | 19 | tot = 0 20 | for p in primes: 21 | if p > n: break 22 | q = n - p 23 | if q in primes: 24 | # print(p, "+", q) 25 | tot += 1 26 | 27 | print(math.ceil(tot / 2)) 28 | 29 | 30 | print("Time:", time.time() - start) 31 | 32 | """ 33 | b) 34 | 3 + 43 35 | 5 + 41 36 | 17 + 29 37 | 23 + 23 38 | 39 | c) 40 | """ 41 | 42 | # code for c 43 | """ 44 | tot = 0 45 | for i in range(5, 50, 2): 46 | valid = False 47 | for p in primes: 48 | if p > i: break 49 | w = i - p 50 | if w in primes: 51 | valid = True 52 | break 53 | if not valid: 54 | tot += 1 55 | 56 | print(tot) 57 | """ 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BIO-Solutions 2 | Solutions for the British Informatics Olympiad Problems 3 | 4 | [BIO Website](https://www.olympiad.org.uk/) 5 | 6 | All Solutions are currently only available in Python 3. 7 | 8 | Solutions work for Part A of each question, with some solutions offering explained solutions for other parts of the question. 9 | 10 | **Interested in personalised tutoring for the BIO or IOI? Don't hesitate to get in contact.** 11 | 12 | Some solutions do not work fast enough, though in some cases this is due to the limitations of the Python 3 language. 13 | 14 | The solutions offered here are only for BIO Round 1. I have written solutions for (almost) all of the BIO Final problems. Please contact if interested. 15 | 16 | If you have solution in any language feel free to send it in. All major languages welcome (within reason) and authors will be credited. 17 | 18 | Currently missing perfect solutions include: 19 | 20 | - Nothing to see here... 21 | 22 | With Thanks To: 23 | 24 | - Isaac Eason (Waves 2003) 25 | - @prantav 26 | - @h1b2k (Egyptian Fractions 1997) 27 | -------------------------------------------------------------------------------- /2021/2021 Q1 - Down Pat.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | from time import time 3 | 4 | def isPat(n): 5 | if len(n) == 1: return True 6 | if len(n) == 2: 7 | return n[0] > n[1] 8 | 9 | for i in range(1, len(n)): 10 | if isPat(n[:i][::-1]) and isPat(n[i:][::-1]): 11 | if min(n[:i]) > max(n[i:]): return True 12 | return False 13 | 14 | a,b = input().split() 15 | start = time() 16 | 17 | if isPat(a): 18 | print("YES") 19 | else: 20 | print("NO") 21 | if isPat(b): 22 | print("YES") 23 | else: 24 | print("NO") 25 | if isPat(a+b): 26 | print("YES") 27 | else: 28 | print("NO") 29 | 30 | #print(time() - start) 31 | 32 | def d(): 33 | # initialise with number of pats for 0 and 1 length strings 34 | pats = [0, 1] 35 | for i in range(2, 25): 36 | a, b = 1, i - 1 37 | counter = 0 38 | while b > 0 and a < i: 39 | counter += pats[a] * pats[b] 40 | a += 1 41 | b -= 1 42 | pats.append(counter) 43 | print(pats[24]) 44 | 45 | 46 | -------------------------------------------------------------------------------- /2004/2004 Q1 - Mayan Calendar.py: -------------------------------------------------------------------------------- 1 | baktun, katun, tun, uinal, kin = list(map(int, input().split())) 2 | 3 | # calibrate to 1 1 2000 4 | baktun -= 13 5 | katun -= 20 6 | tun -= 7 7 | uinal -= 16 8 | kin -= 3 9 | 10 | # convert to kins 11 | katun += 20 * baktun 12 | tun += 20 * katun 13 | uinal += 18 * tun 14 | kin += 20 * uinal 15 | 16 | #print(kin % 7) 17 | # find year 18 | year = 2000 19 | while True: 20 | days = 365 21 | if year % 100 == 0: 22 | if year % 400 == 0: days += 1 23 | elif year % 4 == 0: days += 1 24 | if kin < days: 25 | break 26 | year += 1 27 | kin -= days 28 | 29 | # find month 30 | m = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 31 | 32 | if year % 100 == 0: 33 | if year % 400 == 0: m[2] += 1 34 | elif year % 4 == 0: 35 | m[2] += 1 36 | 37 | month = 1 38 | while True: 39 | if kin < m[month]: 40 | break 41 | kin -= m[month] 42 | month += 1 43 | 44 | print(kin+1, month, year) 45 | 46 | """ 47 | b) 13 20 7 17 14, 13 20 8 16 9 48 | 49 | c) 2,880,000 days 50 | 1012737 days after 1 1 2000 (Sat) so 5 days after Sat so it is a Thursday 51 | """ -------------------------------------------------------------------------------- /2008/2008 Q2 - Enigma Machine.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | rotor1 = deque([0, 2, 3, 3]) 4 | rotor2 = deque([0, 1, 3, 0]) 5 | botor1 = deque([0, 1, 1, 2]) 6 | reflector = {0:3, 1:2, 2:1, 3:0} 7 | letters = list("ABCD") 8 | 9 | n = int(input()) 10 | 11 | rotor1.rotate(n % 4) 12 | botor1.rotate(n % 4) 13 | rotor2.rotate((n//4) % 4) 14 | 15 | text = input() 16 | 17 | for letter in text: 18 | q = letters.index(letter) 19 | q = (q + rotor1[q]) % 4 20 | q = (q + rotor2[q]) % 4 21 | q = reflector[q] 22 | q = (q + rotor2[q]) % 4 23 | q = (q + botor1[q]) % 4 24 | print(letters[q], end="") 25 | 26 | # rotate 27 | 28 | n += 1 29 | rotor1.rotate(1) 30 | botor1.rotate(1) 31 | if n % 4 == 0: 32 | rotor2.rotate(1) 33 | 34 | """ 35 | b) BCBCDB 36 | 37 | c) 38 | 39 | d) No. Each port is connected to one wire, which is connect to a port on the other side. Therefore each letter is 40 | connected to exactly one other letter and because they are wired, the wirings go both ways. A letter is encrypted to 41 | the letter it is wired to. So if A was coded to B, then B would have to be coded to A. 42 | """ -------------------------------------------------------------------------------- /2011/2011 Q3 - Upside-Down.py: -------------------------------------------------------------------------------- 1 | n = int(input()) 2 | 3 | llist = list("123456789") 4 | rlist = llist[::-1] 5 | 6 | def func(n, left, right): 7 | if n == 0: 8 | return left + right 9 | if n == 1: 10 | return left + "5" + right 11 | 12 | m = 0 13 | while n >= 2 * 9**m: 14 | m += 1 15 | # m -= 1 16 | n -= 2 17 | # print(n, m, 9**m) 18 | left = llist[(n % 9)] + left 19 | right += rlist[(n % 9)] 20 | 21 | return func(n // 9, left, right) 22 | 23 | 24 | 25 | print(func(n, "", "")) 26 | 27 | 28 | """ 29 | b) 8 * 6 * 4 * 2 = 384 30 | 31 | c) 38, 54747259285257139791317935852815836365 32 | 33 | d) More with 1001 digits. 1001 is an odd number and so every every upside-down number of that length has 5 as its middle 34 | digit. 1000 is an even number and so not every upside-down number of that length will contain a 5. 35 | 36 | By adding a 5 to the centre of a 1000 digit upside-down number creates a 1001 digit upside-down number. Thus, each 37 | 1000 digit upside-down number creates a specific 1001 digit upside-down number, meaning there are the same number of 38 | 1000 digit and 1001 digit upside-down numbers. 39 | """ -------------------------------------------------------------------------------- /2005/2005 Q2 - Turing Machine.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | import time 3 | tape = deque([0 for i in range(1000000)]) 4 | state = 1 5 | 6 | n = int(input()) 7 | states = [input().split() for i in range(n)] 8 | m = int(input()) 9 | 10 | start = time.time() 11 | 12 | 13 | for p in range(m): 14 | info = states[state - 1][tape[0]] 15 | tape[0] = int(info[0]) 16 | if info[1] == "L": tape.rotate(1) 17 | else: tape.rotate(-1) 18 | state = int(info[2]) 19 | if state == 0: break 20 | 21 | # print out tape section 22 | tape.rotate(3) 23 | for i in range(7): 24 | print(tape.popleft(), end="") 25 | print() 26 | print(p+1) 27 | print("Time:", time.time() - start) 28 | 29 | 30 | """ 31 | b) n+m 1s in a row (test it with a range of ns and ms to see) 32 | e.g. [1 for i in range(4)] + [0] + [1 for i in range(5)] 33 | 34 | c) 35 | 2 36 | 1L2 1R2 37 | 1R1 1L0 38 | 39 | d) No. A special rule is only necessary if both machines are attempting to write to the same cell. If the start machines 40 | are a odd number of cells apart (i.e. adjacent) they will always be an odd number of spaces apart. This is because each 41 | turn the machines move to an adjacent cell, and so must always be an odd number of spaces apart. 42 | 43 | """ -------------------------------------------------------------------------------- /1999/1999 Q1 - Digital Rivers.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def generateRiver(n, l): 4 | river = set() 5 | for i in range(l): 6 | river.add(n) 7 | n += sum(list(map(int, list(str(n))))) 8 | return river 9 | 10 | def nextRiver(n): 11 | return n + sum(list(map(int, list(str(n))))) 12 | 13 | maxL = 9999 14 | 15 | n = int(input()) 16 | 17 | start = time.time() 18 | 19 | river1 = generateRiver(1,maxL) 20 | river3 = generateRiver(3,maxL) 21 | river9 = generateRiver(9,maxL) 22 | 23 | while True: 24 | if n in river1: 25 | print("First meets river 1 at", n) 26 | break 27 | if n in river3: 28 | print("First meets river 3 at", n) 29 | break 30 | if n in river9: 31 | print("First meets river 9 at", n) 32 | break 33 | n = nextRiver(n) 34 | 35 | print("Time:", time.time() - start) 36 | 37 | """ 38 | b) 416 39 | """ 40 | 41 | def b(n): 42 | rivers = [] 43 | for i in range(n*5): 44 | rivers.append(generateRiver(i, 999)) 45 | i = 8 46 | while True: 47 | c = 0 48 | # print(i) 49 | for river in rivers: 50 | if i in river: c += 1 51 | if c >= 100: 52 | return i 53 | i += 1 54 | print(b(100)) 55 | -------------------------------------------------------------------------------- /2003/2003 Q1 - ISBN.py: -------------------------------------------------------------------------------- 1 | isbn = list(input()) 2 | 3 | def isbnsolve(isbn): 4 | tot = 0 5 | index = 0 6 | for i in range(len(isbn)): 7 | item = isbn[i] 8 | if item != "?": 9 | if item == "X": 10 | item = 10 11 | else: 12 | item = int(item) 13 | tot += item * (10-i) 14 | else: 15 | index = (10-i) 16 | 17 | done = False 18 | for j in range(10): 19 | if ((j * index) + tot) % 11 == 0: 20 | return j 21 | done = True 22 | break 23 | 24 | if not done: 25 | return "X" 26 | 27 | print(isbnsolve(isbn)) 28 | 29 | # part c 30 | bn = "3201014525" 31 | pos = set() 32 | for i in range(10): 33 | for j in range(10): 34 | if i < j: 35 | a = list(bn) 36 | b = a[i] 37 | c = a[j] 38 | a[j] = b 39 | a[i] = c 40 | ans = a[-1] 41 | a[-1] = "?" 42 | x = "".join(a) 43 | print(x, ans, isbnsolve(x)) 44 | if str(isbnsolve(x)) == ans: 45 | pos.add(x) 46 | print(pos) 47 | 48 | 49 | 50 | """ 51 | b) 3540678654, 9514451570 52 | c) 2301014525, 3401012525, 0201314525, 3200114525 53 | """ 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /2017/2017 Q1 - Coloured Triangles.py: -------------------------------------------------------------------------------- 1 | row = list(input()) 2 | 3 | def triangle(row, size): 4 | while len(row) > size: 5 | newRow = [] 6 | for i in range(len(row) - 1): 7 | if row[i] == row[i+1]: newRow.append(row[i]) 8 | else: 9 | colours = [row[i], row[i+1]] 10 | if "G" not in colours: newRow.append("G") 11 | elif "B" not in colours: newRow.append("B") 12 | elif "R" not in colours: newRow.append("R") 13 | row = list(newRow) 14 | return row[0] 15 | 16 | print(triangle(row, 1)) 17 | """ 18 | b) there are 3 19 | RRRBBGGRG 20 | BGBRGBRGR 21 | GBGGRRBBB 22 | 23 | c) There is only 1 way. Given a complete row, there are 3 different options for the row directly above, but only 1 option 24 | for each of the rows below. However, if 1 value in the row directly above is known, there is only 1 option for that row. 25 | If 1 colour in each row is known, then the bottom row must be fully known as it only contains 1 value. Therefore, 26 | each row can be completed sequentially from the bottom. 27 | 28 | d) Any value of the form 3^k + 1 as there are 3 possible values and the 2nd row must contain 3^k numbers? 29 | e.g. 10 30 | 31 | 32 | d) 33 | 34 | """ 35 | -------------------------------------------------------------------------------- /1997/1997 Q1 - Time.py: -------------------------------------------------------------------------------- 1 | h,m = list(map(int, input().replace(":", " ").split())) 2 | hours = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"] 3 | minutes = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", 4 | "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen", "Twenty", 5 | "Twenty-one", "Twenty-two", "Twenty-three", "Twenty-four", "Twenty-five", "Twenty-six", "Twenty-seven", "Twenty-eight", 6 | "Twenty-nine"] 7 | if m < 30: 8 | h = hours[h - 1] 9 | if m == 0: 10 | print(minutes[hours.index(h)] + " o'clock") 11 | elif m == 15: 12 | print("Quarter past " + h) 13 | elif m == 1: 14 | print("One minute past " + h) 15 | else: 16 | print(minutes[m-1] + " minutes past " + h) 17 | elif m == 30: 18 | print("Half past " + h) 19 | else: 20 | h = hours[h%len(hours)] 21 | m = 60 - m 22 | if m == 15: 23 | print("Quarter to " + h) 24 | elif m == 1: 25 | print("One minute to " + h) 26 | else: 27 | print(minutes[m-1] + " minutes to " + h) 28 | 29 | """ 30 | b) 11:23, 11:27, 11:28, 12:23, 12:27, 12:28 31 | Longest forms will be x minutes past y, why x and y are maximum lengths. 32 | x = 23, 27, 28 33 | y = 11, 12 34 | 35 | """ 36 | -------------------------------------------------------------------------------- /2018/2018 Q1 - Debt Repayment.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | 3 | 4 | interest, repayment = list(map(int, input().split())) 5 | 6 | def payOffDebt(interest, repayment): 7 | debt = 100 * 100 8 | paid = 0 9 | 10 | interest /= 100 11 | repayment /= 100 12 | numPayments = 0 13 | lastDebt = debt 14 | 15 | while debt: 16 | # print(debt, interest, repayment) 17 | debt += debt * interest 18 | debt = ceil(debt) 19 | pay = min(debt, max(debt * repayment, 5000)) 20 | pay = ceil(pay) 21 | debt -= pay 22 | debt = ceil(debt) 23 | paid += pay 24 | numPayments += 1 25 | 26 | if debt >= lastDebt: return 0 27 | lastDebt = debt 28 | 29 | 30 | return paid / 100 31 | 32 | print(payOffDebt(interest, repayment)) 33 | #print(numPayments) 34 | 35 | """ 36 | b) 5 37 | 38 | c) 96% interest, 49% repayment, gives 107296.43 repaid 39 | 40 | """ 41 | 42 | def c(): 43 | largest = 0 44 | ans = (0, 0) 45 | for i in range(0,100): 46 | print(i) 47 | for r in range(1,100): 48 | tot = payOffDebt(i, r) 49 | # print(tot, i, r) 50 | if tot > largest: 51 | largest = tot 52 | ans = (i,r) 53 | print(ans[0], ans[1]) 54 | 55 | #c() 56 | 57 | -------------------------------------------------------------------------------- /2022/2022 Q1 - Decrypt.py: -------------------------------------------------------------------------------- 1 | alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 2 | conv = dict() 3 | for i in range(26): 4 | conv[alpha[i]] = i + 1 5 | 6 | def decrypt(code): 7 | 8 | ans = "" 9 | code = code[::-1] 10 | 11 | for i in range(len(code) - 1): 12 | a = conv[code[i]] - conv[code[i+1]] 13 | if a <= 0: a += 26 14 | ans += alpha[a-1] 15 | 16 | ans += code[-1] 17 | ans = ans[::-1] 18 | return ans 19 | 20 | def encrypt(code): 21 | 22 | ans = code[0] 23 | 24 | for i in range(1, len(code)): 25 | a = conv[code[i]] + conv[ans[-1]] 26 | if a > 26: a -= 26 27 | ans += alpha[a-1] 28 | 29 | return ans 30 | 31 | def count_cycle(code): 32 | word = encrypt(code) 33 | i = 1 34 | while word != code: 35 | word = encrypt(word) 36 | i += 1 37 | return i 38 | 39 | code = input().upper() 40 | print(encrypt(code)) 41 | code = encrypt(code) 42 | print(decrypt(code)) 43 | 44 | # c) 45 | #print(count_cycle("OLYMPIAD")) 46 | 47 | def d(): 48 | num = 999999999999 49 | ans = 0 50 | for l1 in alpha: 51 | for l2 in alpha: 52 | for l3 in alpha: 53 | word = l1 + l2 + l3 54 | cycle = count_cycle(word) 55 | if num % cycle == 0: ans += 1 56 | print(ans) 57 | 58 | #d() 59 | -------------------------------------------------------------------------------- /2004/2004 Q3 - Morse Code.py: -------------------------------------------------------------------------------- 1 | import time 2 | from functools import lru_cache 3 | 4 | """ 5 | NOTE THAT THIS SOLUTION IS A LITTLE BIT TOO SLOW IN SOME TEST CASES 6 | """ 7 | 8 | morse = [".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", 9 | ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."] 10 | alpha = list("abcdefghijklmnopqrstuvwxyz") 11 | 12 | toMorse = dict(zip(alpha, morse)) 13 | toAlpha = dict(zip(morse, alpha)) 14 | 15 | word = input() 16 | 17 | start = time.time() 18 | 19 | m = "" 20 | for letter in word: m += toMorse[letter] 21 | #print(m) 22 | 23 | size = len(word) 24 | check = True 25 | 26 | #@lru_cache(maxsize=None) 27 | def func(word, remainder): 28 | global m, check 29 | tot = 0 30 | if (len(remainder)) > (size - len(word)) * 4: return 0 31 | for i in range(min(4, len(remainder))): 32 | if remainder[:i+1] in morse: 33 | w = word + toAlpha[remainder[:i+1]] 34 | r = remainder[i+1:] 35 | if len(r) == 0: 36 | if len(w) == size: 37 | tot += 1 38 | # print(remainder, word) 39 | else: 40 | tot += func(w,r) 41 | return tot 42 | 43 | print(func("", m)) 44 | 45 | print("Time:", time.time() - start) 46 | 47 | """ 48 | b) ----- = 13 49 | -..-----. = 170 50 | c) 124 51 | """ 52 | 53 | 54 | -------------------------------------------------------------------------------- /2006/2006 Q3 - Drats.py: -------------------------------------------------------------------------------- 1 | import time 2 | from functools import lru_cache 3 | 4 | s, d = list(map(int, input().split())) 5 | start = time.time() 6 | 7 | c = 0 8 | 9 | @lru_cache(maxsize=None) 10 | def perm_drats(drats, score): 11 | # print(drats, score) 12 | if drats == 0 and score == 0: 13 | return 1 14 | elif drats <= 0 or score <= 0: 15 | return 0 16 | return sum([perm_drats(drats - 1, score - i) for i in range(1,21)]) 17 | 18 | print(sum([perm_drats(d - 1, s - i*2) for i in range(1,21)])) 19 | 20 | """ 21 | b) 61 for 2; 81 for 3 22 | 23 | c) 510176 24 | 25 | d) No. Numbers which are high and even must be next to low and odd numbers and vice versa. If a board contains high and 26 | even numbers, it cannot therefore contain high and odd or low and even numbers. The numbers 1 to 20 contain numbers 27 | which are low and odd, low and even, high and odd, and high and even. 28 | 29 | """ 30 | 31 | # code for c 32 | """ 33 | from itertools import combinations, permutations 34 | combs = list(combinations([i for i in range(1,21)], 6)) 35 | print(len(combs)) 36 | tot = 0 37 | c = 0 38 | for it in combs: 39 | c += 1 40 | if c % 100 == 0: print(c) 41 | p = permutations(it) 42 | for item in list(p): 43 | a = item[0] * 2 + item[1] + item[2] 44 | b = item[3] * 2 + item[4] + item[5] 45 | # print(a,b) 46 | if a == b: 47 | tot += 1 48 | print(tot) 49 | """ 50 | -------------------------------------------------------------------------------- /2020/2020 Q1 - Roman Look-and-Say.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | number, n = input().split() 4 | n = int(n) 5 | 6 | start = time.time() 7 | 8 | def to_numeral(n): 9 | numerals = {1:"I", 5:"V", 10:"X", 50:"L", 100:"C", 500:"D", 1000:"M"} 10 | num = "" 11 | while n > 0: 12 | for item in sorted(numerals.keys())[::-1]: 13 | if n >= item: 14 | num += numerals[item] 15 | n -= item 16 | break 17 | return num.replace("DCCCC", "CM").replace("CCCC", "CD").replace("LXXXX", "XC").replace("XXXX", "XL").replace("VIIII", "IX").replace("IIII", "IV") 18 | 19 | 20 | def look_and_say(n): 21 | new = "" 22 | curr = "" 23 | count = 0 24 | for l in n: 25 | if l != curr: 26 | if count > 0: 27 | new += to_numeral(count) + curr 28 | curr = l 29 | count = 1 30 | else: 31 | count += 1 32 | return new + to_numeral(count) + curr 33 | 34 | for i in range(n): 35 | number = look_and_say(number) 36 | # print(number) 37 | 38 | print(number.count("I"), number.count("V")) 39 | print("Time:", time.time() - start) 40 | 41 | """ 42 | b) II, III, IV, IX 43 | 44 | c) 3919 45 | 46 | """ 47 | 48 | def c(): 49 | seen = set() 50 | for i in range(1,4000): 51 | numeral = to_numeral(i) 52 | seen.add(look_and_say(numeral)) 53 | print(len(seen)) 54 | 55 | #c() 56 | -------------------------------------------------------------------------------- /2018/2018 Q2 - Decoder Ring.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | alpha = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 4 | 5 | def generateDial(n): 6 | letters = deque(alpha) 7 | dial = deque() 8 | while letters: 9 | letters.rotate(-(n-1)) 10 | dial.append(letters.popleft()) 11 | return dial 12 | 13 | n,word = input().split() 14 | 15 | dial = generateDial(int(n)) 16 | print("".join(list(dial)[:6])) 17 | 18 | ans = "" 19 | for letter in word: 20 | ans += list(dial)[alpha.index(letter)] 21 | dial.rotate(-1) 22 | 23 | print(ans) 24 | 25 | """ 26 | b) LKBXIY 27 | 28 | c) No. The nth letter of the word will be encrypted after n-1 rotations of the dial. The nth letter of the alphabet 29 | will thus be encrypted as the 2i-1st letter on the second dial. It follows that the nth encrypted letter will always match the 30 | n+13th encrypted letter. As the letters repeat, only the letters on odd positions of the second dial will be expressed 31 | in the encrypted word, each appearing twice. 32 | 33 | d) 1260 34 | The letters within the word will return to the original letter in cycles. E.g. A -> B -> C -> A. To get the length of 35 | the longest cycle for the entire word, we must find a set of cycles that return a maximum longest cycle. As each letter 36 | must appear in a cycle, the lengths of the individual cycles must sum to 26. Cycles that are co-prime will give the 37 | greatest total length. 38 | 4 + 9 + 5 + 7 + 1 = 27 39 | 2 * 2 * 3 * 3 * 5 * 7 = 1260 40 | """ 41 | -------------------------------------------------------------------------------- /2005/2005 Q3 - Movie Magic.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | import time 3 | 4 | orders = set() 5 | 6 | a = int(input()) 7 | 8 | scenesTodo = list(map(int, input().split())) 9 | 10 | start = time.time() 11 | 12 | 13 | allScenes = len(scenesTodo) 14 | 15 | @lru_cache(maxsize=None) 16 | def scenes(scenesLeft, scenesDone): 17 | # print(scenesLeft, scenesDone) 18 | if sum(scenesLeft) == 1: return 1 19 | tot = [] 20 | for actor in range(a): 21 | if scenesLeft[actor] > 0: 22 | if actor == 0 or scenesDone[actor-1] > scenesDone[actor]: 23 | d = list(scenesLeft) 24 | d[actor] -= 1 25 | l = list(scenesDone) 26 | l[actor] += 1 27 | tot.append(scenes(tuple(d), tuple(l))) 28 | return sum(tot) 29 | 30 | 31 | blank = tuple([0 for i in range(a)]) 32 | print(scenes(tuple(scenesTodo), blank)) 33 | print("Time:", time.time() - start) 34 | 35 | """ 36 | b) 6 (2 actors, 2 scenes) 37 | a,A,b,B 38 | A,a,b,B 39 | a,A,B,b 40 | A,a,B,b 41 | a,b,A,B 42 | A,B,a,b 43 | 1680 (3 actors, 3 scenes) 44 | 45 | c) If the junior actor's scene must be the last scene shot, then each actor must have the same number of scenes, as if 46 | the junior actor's last scene wasn't the last scene shot, then the hierarchy would be broken. The hierarchy means that 47 | the most junior actor cannot have shot more scenes than any other actor. If the actors have different numbers of scenes, 48 | there must be an actor with more scenes than the most junior actor, and so they may film their last scene after the most 49 | junior actor has shot all of their scenes. 50 | 51 | d) If 52 | 53 | """ 54 | -------------------------------------------------------------------------------- /2014/2014 Q3 - Increasing Passwords.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | from math import comb 3 | 4 | alpha = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") 5 | 6 | def password(n, word, length): 7 | if length <= 0: 8 | return word 9 | if len(word) == 0: 10 | a = 0 11 | else: 12 | a = alpha.index(word[-1]) + 1 13 | 14 | for letter in range(a, len(alpha)): 15 | perms = comb(36 - letter - 1, length-1) 16 | # print(letter, perms, n, length) 17 | if n <= perms: 18 | return password(n, word + alpha[letter], length-1) 19 | n = n - perms 20 | 21 | def getLength(n): 22 | l = 1 23 | # print(getLen(36, l)) 24 | # while n > getLen(36, l): 25 | while n > comb(36, l): 26 | n -= comb(36, l) 27 | l += 1 28 | return l,n 29 | 30 | n = int(input()) 31 | 32 | l,n = getLength(n) 33 | print(password(n, "", l)) 34 | 35 | """ 36 | b) 14, BIO, NTU, BIO14, ABCDE 37 | 38 | c) 68719476734 39 | passwords up to length of 36 will be accepted 40 | 41 | d) There is only 1 pair that fulfills this criteria: ATUVWXYZ0123456789 and BCDEFGHIJKLMNOPQRS. Given a password, 42 | the next password will either have the same number of digits or 1 more digit. For the number of digits between the 43 | passwords to be 36, without repeats, both passwords must contain 18 digits. As the passwords are ordered, the first 44 | password must contain the A. As the second password cannot contain an A, the first password must be the last password 45 | beginning with A. 46 | 47 | """ 48 | 49 | def c(): 50 | ans = 0 51 | for length in range(1,36): 52 | ans += comb(36, length) 53 | print(ans) 54 | -------------------------------------------------------------------------------- /2015/2015 Q1 - Block Palindromes.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | seen = set() 4 | allSeen = set() 5 | def palindrome(blocks): 6 | done = True 7 | 8 | b = tuple(blocks) 9 | if b in allSeen: 10 | return blocks 11 | 12 | allSeen.add(b) 13 | 14 | 15 | if blocks == blocks[::-1]: 16 | seen.add(b) 17 | 18 | for item in blocks: 19 | if len(item) > 1: 20 | done = False 21 | break 22 | if done: 23 | return blocks 24 | 25 | for i in range(len(blocks)): 26 | for j in range(1,len(blocks[i])): 27 | new = list(blocks) 28 | w = new.pop(i) 29 | a = w[:j] 30 | b = w[j:] 31 | new.insert(i, b) 32 | new.insert(i, a) 33 | palindrome(new) 34 | 35 | a = input() 36 | start = time.time() 37 | palindrome([a]) 38 | print(seen) 39 | print(len(seen) - 1) 40 | print("Time:", time.time() - start) 41 | 42 | """ 43 | b) ('A', 'ABCBA', 'A'), 44 | ('AA', 'BCB', 'AA'), 45 | ('AA', 'B', 'C', 'B', 'AA'), 46 | ('A', 'A', 'B', 'C', 'B', 'A', 'A'), 47 | ('A', 'A', 'BCB', 'A', 'A') 48 | 49 | c) If all the groupings contain an even number of block, then the block palindrome contains an even number of letters. 50 | When a block palindrome is reversed, each block in the first half of the word is repeated in the second half. So there 51 | are 2 * n blocks, which is even. Also, there must only be 1 possible grouping. If there is more than 1 possible grouping, 52 | then at least grouping must contain more than 2 blocks. Any grouping that contains more than 2 blocks can be changed into 53 | a grouping with the first block, the last blocks and the other blocks grouped into 1 block, which gives an odd number of 54 | blocks. 55 | 56 | """ 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /1999/1999 Q3 - Playing Games.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | import time 3 | 4 | n = int(input()) 5 | points = set(map(int, input().split())) 6 | 7 | @lru_cache(maxsize=None) 8 | def sumFactors(n): 9 | global points 10 | factors = [] 11 | for i in range(min(points),n//2+1): 12 | factors.append((i, n-i)) 13 | return factors 14 | 15 | m = int(input()) 16 | scores = list(map(int, input().split())) 17 | start = time.time() 18 | 19 | #print(points) 20 | 21 | @lru_cache(maxsize=None) 22 | def minPoints(n): 23 | global points 24 | # print(n) 25 | if n == 0 or n in points: 26 | return 1, [n] 27 | ans = [] 28 | items = [] 29 | for pair in sumFactors(n)[::-1]: 30 | tot = minPoints(pair[0])[0] + minPoints(pair[1])[0] 31 | ans.append(tot) 32 | # print(pair[0], minPoints(pair[0])) 33 | items.append(minPoints(pair[0])[1] + minPoints(pair[1])[1]) 34 | if len(sumFactors(n)) == 0: 35 | return float("inf"), [] 36 | return min(ans), items[ans.index(min(ans))] 37 | 38 | for s in scores: 39 | ans = minPoints(s) 40 | if ans[0] == float("inf"): 41 | print("Impossible") 42 | else: 43 | print(ans[0], end=" ") 44 | for item in set(ans[1]): 45 | print(str(ans[1].count(item)) + "x" + str(item), end=" ") 46 | print() 47 | 48 | print("Time:", time.time() - start) 49 | 50 | """ 51 | b) For difference of 80 - 2 rounds between them ([1], [81]) 52 | For difference of 50 - 4 rounds ([1,3,27 = 41], [81]) 53 | 54 | c) 1333 55 | """ 56 | 57 | points = [1,4,5,17,28,43,100] 58 | def c(last, score): 59 | if score == 100: return 1 60 | if score > 100: return 0 61 | tot = [] 62 | for item in points: 63 | if item >= last: 64 | tot.append(c(item, score + item)) 65 | return sum(tot) 66 | #print(c(0, 0)) -------------------------------------------------------------------------------- /2019/2019 Q2 - Trail.py: -------------------------------------------------------------------------------- 1 | from _collections import defaultdict 2 | import time 3 | 4 | 5 | def explore(t, instructions, m, partd=False): 6 | trail = defaultdict(int) 7 | pos = (0, 0) 8 | d = 0 9 | # 0 - up, 1 - right, 2 - down, 3 - left 10 | right = {0: 1, 1: 2, 2: 3, 3: 0} 11 | left = {1: 0, 0: 3, 3: 2, 2: 1} 12 | dir = [(0, 1), (1, 0), (0, -1), (-1, 0)] 13 | 14 | for i in range(m): 15 | if not partd: 16 | toRemove = [] 17 | for item in trail: 18 | trail[item] -= 1 19 | if trail[item] == 0: 20 | toRemove.append(item) 21 | for item in toRemove: 22 | trail.pop(item) 23 | trail[pos] = t 24 | # print(len(trail)) 25 | move = instructions[i%len(instructions)] 26 | if move == "L": 27 | d = left[d] 28 | elif move == "R": 29 | d = right[d] 30 | 31 | moved = False 32 | 33 | for _ in range(4): 34 | if (pos[0] + dir[d][0], pos[1] + dir[d][1]) not in trail: 35 | moved = True 36 | pos = (pos[0] + dir[d][0], pos[1] + dir[d][1]) 37 | break 38 | d = right[d] 39 | 40 | if not moved: 41 | # part d 42 | print("stuck") 43 | if partd: return i 44 | break 45 | return pos 46 | 47 | 48 | t, instructions, m = input().split() 49 | t, m = int(t), int(m) 50 | 51 | start = time.time() 52 | 53 | print(explore(t,instructions,m)) 54 | #print(trail) 55 | print("Time:", time.time() - start) 56 | 57 | """ 58 | b) 00000 59 | 00345 60 | 01206 61 | 0D017 62 | 00000 63 | 64 | c) 21 * 21 - 1 = 440 65 | 66 | d) 67 | """ 68 | #print(explore(1, "LLRFFF", 9999999, partd=True)) 69 | -------------------------------------------------------------------------------- /1997/1997 - Egyptian Fractions - FULL MARKS.py: -------------------------------------------------------------------------------- 1 | # With thanks to @h1b2k 2 | 3 | import sys 4 | import math 5 | from fractions import Fraction 6 | 7 | solutions = [] 8 | 9 | 10 | def remove_duplicates(lst): 11 | return [list(x) for x in dict.fromkeys(tuple(sorted(x, reverse=True)) for x in lst)] 12 | 13 | 14 | def optimize(lst): 15 | return [sublist for (min_frac, sublist) in [(min(sublist), sublist) for sublist in lst] if min_frac == max([(min(sublist), sublist) for sublist in lst], key=lambda x: x[0])[0]] 16 | 17 | 18 | def recursive_chunk(f, max_depth, path=[]): 19 | start = max(math.floor(f.denominator / f.numerator), path[-1].denominator + 1 if path else 1) 20 | 21 | for i in range(start, 32001): 22 | unit = Fraction(1, i) 23 | if unit * (max_depth - len(path)) < f: 24 | break 25 | 26 | new_path = path + [unit] 27 | if unit == f: 28 | solutions.append(new_path) 29 | return 30 | elif unit < f and len(new_path) <= max_depth: 31 | recursive_chunk(f - unit, max_depth, new_path) 32 | 33 | 34 | def main(): 35 | num, den = map(int, input("input: ").split()) 36 | fra = Fraction(num, den) 37 | 38 | solutions.clear() 39 | for depth in range(1, num + 1): 40 | sys.stdout.write(f"\rdepth: {depth} ") 41 | sys.stdout.flush() 42 | recursive_chunk(fra, depth) 43 | if solutions: 44 | break 45 | 46 | sys.stdout.write(f"\r") 47 | 48 | if solutions: 49 | filtered = remove_duplicates(solutions) 50 | optimized = optimize(filtered) 51 | for s in optimized: 52 | print(' '.join(str(f.denominator) for f in s)) 53 | else: 54 | print("no solutions found.") 55 | 56 | 57 | if __name__ == "__main__": 58 | while True: 59 | try: 60 | main() 61 | except Exception as e: 62 | print(f"Error: {e}") 63 | -------------------------------------------------------------------------------- /2020/2020 Q3 - False Plan.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | import time 3 | 4 | letters = "" 5 | alpha = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 6 | row = 2 7 | 8 | order = [] 9 | @lru_cache(maxsize=None) 10 | def plan(last, n): 11 | global letters, row 12 | # print(last,n,row) 13 | 14 | if len(last) > row: return 0 15 | if n == 0: 16 | # print(tot) 17 | return 1 18 | ans = [] 19 | for l in letters: 20 | if len(last) > 0 and l == last[0]: 21 | if len(last) < row: 22 | ans.append(plan(last + l, n-1)) 23 | else: 24 | ans.append(plan(l, n-1)) 25 | return sum(ans) 26 | 27 | p,q,r = list(map(int, input().split())) 28 | letters = alpha[:p] 29 | row = q 30 | n = int(input()) 31 | start = time.time() 32 | 33 | def build(word, curr, n, length): 34 | global letters, row, p, q, r 35 | if len(word) == r: return word 36 | # print(word) 37 | for l in letters: 38 | a = curr + l 39 | if l not in curr: a = l 40 | if n <= plan(a, length-1): 41 | if len(curr) <= row: 42 | return build(word + l, a, n, length-1) 43 | # else: n += 1 44 | else: 45 | n -= plan(a, length-1) 46 | return word 47 | 48 | print(plan("", "", r)) 49 | print(build("", "", n, r)) 50 | print("Time:", time.time() - start) 51 | 52 | """ 53 | b) 39, 29947 54 | 55 | c) If a plan is in the same position when ordered forwards or backwards, the number of letters used (p) must be odd. The 56 | nth plan must right at the middle of the list if its position is the same when ordered forwards or backwards, so there 57 | must be an odd number of plans. However, the length of the list of plans which contain more than 1 letter will always be 58 | even. Also, r <= q, in order to achieve the length of plans so that they only contain 1 letter each. 59 | """ 60 | -------------------------------------------------------------------------------- /2008/2008 Q3 - Shirts.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | target = list("1234567") 4 | 5 | order = list(input()) 6 | 7 | start = time.time() 8 | 9 | pending = [(order, 0)] 10 | 11 | ans = set() 12 | seen = set() 13 | while True: 14 | item, d = pending.pop(0) 15 | 16 | # code for part b 17 | """ 18 | if d == 6: 19 | ans.add("".join(item)) 20 | if d > 6: break 21 | """ 22 | 23 | if item == target: 24 | print(d) 25 | break 26 | 27 | # 1 28 | q = list(item) 29 | a = q.pop(0) 30 | q.insert(3, a) 31 | if "".join(q) not in seen: 32 | seen.add("".join(q)) 33 | pending.append((q, d+1)) 34 | 35 | # 2 36 | q = list(item) 37 | a = q.pop(-1) 38 | q.insert(3, a) 39 | if "".join(q) not in seen: 40 | seen.add("".join(q)) 41 | pending.append((q, d+1)) 42 | 43 | # 3 44 | q = list(item) 45 | a = q.pop(3) 46 | q.insert(0, a) 47 | if "".join(q) not in seen: 48 | seen.add("".join(q)) 49 | pending.append((q, d+1)) 50 | 51 | # 4 52 | q = list(item) 53 | a = q.pop(3) 54 | q.append(a) 55 | if "".join(q) not in seen: 56 | seen.add("".join(q)) 57 | pending.append((q, d+1)) 58 | 59 | #print(len(ans)) 60 | print("Time:", time.time() - start) 61 | 62 | """ 63 | b) 11 for 2 64 | functions 1 and 3, 2 and 4 are inverse of each other. So both of those next to each other result in the same ordering. 65 | 1 + 3 * 4 = 13 66 | Also the orders of functions 1 and 2, 3 and 4 don't matter, they result in the same ordering 67 | 13 - 2 = 11 68 | Why is it not 334??? 69 | 70 | 71 | c) 72 | 73 | d) No. It is possible to get to the given starting order from any other starting order and so finishing in a different 74 | order is equivalent to freely rearranging the shirts in some way. Thus, choosing the start and finishing orders has 75 | no bearing on the length of the hardest ordering as the changing of the arrangements is equivalent. 76 | """ 77 | -------------------------------------------------------------------------------- /2005/2005 Q1 - Fractions.py: -------------------------------------------------------------------------------- 1 | n = input() 2 | q = len(n) - 2 3 | n = int(n[2:]) 4 | b = 10 ** q 5 | primes = [2] 6 | 7 | 8 | def primeFactorise(n): 9 | ans = [] 10 | while n not in primes: 11 | for prime in primes: 12 | if n % prime == 0: 13 | ans.append(prime) 14 | n /= prime 15 | break 16 | ans.append(int(n)) 17 | return ans 18 | 19 | 20 | def convertToFraction(a,b): 21 | 22 | for i in range(3, 10000): 23 | prime = True 24 | for p in primes: 25 | if p > i**0.5: break 26 | if i % p == 0: 27 | prime = False 28 | break 29 | if prime: primes.append(i) 30 | 31 | top = primeFactorise(n) 32 | bottom = primeFactorise(b) 33 | 34 | t = [] 35 | #print(top, bottom) 36 | 37 | for item in top: 38 | if item in bottom: 39 | z = bottom.count(item) 40 | z -= 1 41 | bottom.remove(item) 42 | # for i in range(z): bottom.append(item) 43 | else: 44 | t.append(item) 45 | 46 | #print(n, b) 47 | #print(t, bottom) 48 | 49 | numerator = 1 50 | denominator = 1 51 | for item in t: numerator *= item 52 | for item in bottom: denominator *= item 53 | 54 | return str(numerator) + " / " + str(denominator) 55 | print(convertToFraction(n, b)) 56 | 57 | """ 58 | b) 24 (Number of factors of 10000 excluding 1 59 | 60 | c) 0.9584 61 | Greatest Product is Denominator: 625 62 | Greatest Product of Numerator contains as many 9s as possible: 599 63 | 5*9*9*6*2*5 = 24300 64 | """ 65 | 66 | print("C!") 67 | # code for c 68 | best = 0 69 | high = 0 70 | for i in range(2, 10000): 71 | if i % 100 == 0: 72 | print(i) 73 | print(high, best) 74 | a = convertToFraction(i, 10**4) 75 | score = 1 76 | for j in a: 77 | if j in "0123456789": 78 | score *= int(j) 79 | if score > high: 80 | high = score 81 | best = i 82 | print(high, best) -------------------------------------------------------------------------------- /2015/2015 Q2 - Battleships.py: -------------------------------------------------------------------------------- 1 | 2 | board = [[0 for i in range(10)] for _ in range(10)] 3 | 4 | def draw(): 5 | for y in range(10): 6 | for x in range(10): 7 | print(board[y][x], end="") 8 | print() 9 | 10 | def isValid(x,y): 11 | if x >= 10 or x < 0 or y < 0 or y >= 10: 12 | return False 13 | if board[y][x] == 1: 14 | return False 15 | dir = [(-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,-1)] 16 | for d in dir: 17 | a = x + d[0] 18 | b = y + d[1] 19 | if a < 10 and a >= 0 and b < 10 and b >= 0: 20 | if board[b][a] == 1: return False 21 | return True 22 | 23 | ships = [4, 3, 3, 2, 2, 2, 1, 1, 1, 1] 24 | 25 | a,c,m = list(map(int, input().split())) 26 | r = 0 27 | for ship in ships: 28 | placed = False 29 | while not placed: 30 | r = ((a*r)+c) % m 31 | x = r % 10 32 | y = (r // 10) % 10 33 | print(x,y, r) 34 | r = ((a*r)+c) % m 35 | if r % 2 == 0: 36 | # right 37 | canPlace = True 38 | for i in range(ship): 39 | if not isValid(x+i, y): 40 | canPlace = False 41 | break 42 | if canPlace: 43 | placed = True 44 | for i in range(ship): 45 | board[y][x+i] = 1 46 | print(x,y,"H") 47 | else: 48 | # up (acc down) 49 | canPlace = True 50 | for i in range(ship): 51 | if not isValid(x, y+i): 52 | canPlace = False 53 | break 54 | if canPlace: 55 | placed = True 56 | for i in range(ship): 57 | board[y+i][x] = 1 58 | print(x,y,"V") 59 | # draw() 60 | 61 | """ 62 | b) (3,0), (4,0), (8,0), (7,0) 63 | 64 | c) 18 65 | A square is guaranteed empty if it is next to at least 2 ships 66 | """ 67 | -------------------------------------------------------------------------------- /2019/2019 Q1 - Palindromic Numbers.py: -------------------------------------------------------------------------------- 1 | def startPalindrome(n): 2 | # print(n) 3 | n = str(n) 4 | if len(n) == 1: return n 5 | p = "" 6 | for i in range(len(n) // 2): 7 | # a = max(int(n[i]), int(n[-(i+1)])) 8 | p += n[i] 9 | 10 | if len(n) % 2 == 1: 11 | return p + n[(len(n) // 2) + 1] + p[::-1] 12 | return p + p[::-1] 13 | 14 | def improvePalindrome(number, i, n): 15 | # print(number) 16 | if int("".join(number)) > n: 17 | return "".join(number) 18 | a = int(number[i]) + 1 19 | if a > 9: 20 | number[i] = "0" 21 | number[-(i+1)] = "0" 22 | if i == 0: 23 | number = "".join(number) 24 | number = int(number) + 1 25 | number = startPalindrome(number) 26 | i = len(number) // 2 27 | return improvePalindrome(number, i, n) 28 | return improvePalindrome(number, i-1, n) 29 | number[i] = str(a) 30 | number[-(i+1)] = str(a) 31 | 32 | return improvePalindrome(number, i, n) 33 | 34 | n = int(input()) 35 | q = startPalindrome(n+1) 36 | #print(n, q) 37 | c = len(q) // 2 38 | print(improvePalindrome(list(q), c, n)) 39 | 40 | """ 41 | b) 11 000 000 000 42 | 99 999 999 999 999 999 999 - 99 999 999 988 999 999 999 = 11 000 000 000 43 | 44 | c) 9030 45 | 46 | """ 47 | 48 | def generatePalindromes(n): 49 | palindromes = [] 50 | for i in range(0,n): 51 | a = str(i) + str(i)[::-1] 52 | palindromes.append(int(a)) 53 | a = list(a) 54 | a.pop(len(a) // 2) 55 | palindromes.append(int("".join(a))) 56 | return palindromes 57 | 58 | def c(): 59 | m = 100000 60 | nums = set() 61 | palindromes = generatePalindromes(m // 100) 62 | # print(palindromes) 63 | for a in palindromes: 64 | # print(a) 65 | for b in palindromes: 66 | # print(a+b) 67 | if a <= b and a+b < m: 68 | nums.add(a+b) 69 | print(m - len(nums)) 70 | #c() 71 | 72 | -------------------------------------------------------------------------------- /2012/2012 Q2 - On the Right Track.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | network = {"DA":["E","F"], "CB":["G","H"], "BC":["I","J"], "AD":["K","L"], "AE":["M","N"], "AF":["N","O"], "BG":["O","P"], 4 | "BH":["P","Q"], "CI":["Q","R"], "CJ":["R","S"], "DK":["S","T"], "DL":["T","M"], "VU":["M","N"], "UV":["O","P"], 5 | "WX":["S","T"], "XW":["Q","R"], "EM":["U"], "EN":["U"], "FN":["U"], "FO":["V"], "GO":["V"], "GP":["V"], "HP":["V"], 6 | "HQ":["W"], "IQ":["W"], "IR":["W"], "JR":["W"], "JS":["X"], "KS":["X"], "KT":["X"], "LT":["X"], "LM":["U"], 7 | "MU":["V"], "NU":["V"], "OV":["U"], "PV":["U"], "QW":["X"], "RW":["X"], "SX":["W"], "TX":["W"], "UM":["L","E"], 8 | "UN":"EF", "VO":"FG", "VP":"GH", "WQ":"HI", "WR":"IJ", "XS":"JK", "XT":"KL", "ME":"A", "NE":"A", "NF":"A", "OF":"A", 9 | "OG":"B", "PG":"B", "PH":"B", "QH":"B", "QI":"C", "RI":"C", "RJ":"C", "SJ":"C", "SK":"D", "TK":"D", "TL":"D", "ML":"D", 10 | "EA":"D", "FA":"D", "GB":"C", "HB":"C", "IC":"B", "JC":"B", "KD":"A", "LD":"A"} 11 | 12 | flipflop = list(input()) 13 | train = list(input()) 14 | n = int(input()) 15 | start = time.time() 16 | 17 | for _ in range(n): 18 | s,e = train 19 | t = "".join(train) 20 | train = e + network[t][0] 21 | if e in flipflop: 22 | network[t] = network[t][::-1] 23 | else: 24 | if len(network[t]) == 1: 25 | reverse = network[t][0] + e 26 | # switch if not already facing 27 | if network[reverse][0] != s: 28 | network[reverse] = network[reverse][::-1] 29 | # print("Changed", reverse, "to", network[reverse]) 30 | 31 | print(train) 32 | 33 | print("Time:", time.time() - start) 34 | 35 | """ 36 | b) V,U,M,L,D,A,E,M,U,V,P 37 | 38 | c) After the train leaves P->V it will enter an infinite loop of 8 points, irrelevant of type of the points or how they 39 | are set. However, the exact location of the cycle is unknown without this information as there are 2 possible cycles it 40 | could enter, as the rail layout is symmetrical: 41 | 42 | d) 43 | """ -------------------------------------------------------------------------------- /2003/2003 Q3 - New Order.py: -------------------------------------------------------------------------------- 1 | n, m = list(map(int, input().split())) 2 | from math import comb 3 | 4 | def order(word, zeros, ones, n): 5 | # print(word) 6 | if ones == 0: 7 | return word + "0" * zeros 8 | if zeros == 0: 9 | return word + "1" * ones 10 | 11 | c = comb(zeros + ones - 1, ones) 12 | if n > c: 13 | n -= c 14 | word += "1" 15 | return order(word, zeros, ones - 1, n) 16 | else: 17 | word += "0" 18 | return order(word, zeros - 1, ones, n) 19 | 20 | 21 | def getZeroes(n, ones): 22 | z = 0 23 | while True: 24 | if comb(ones - 1 + z, ones-1) >= n: 25 | break 26 | n -= comb(ones - 1 + z, ones-1) 27 | z += 1 28 | return z,n 29 | 30 | def ans(n,m): 31 | if m == 0: 32 | return "0" 33 | z,n = getZeroes(n,m) 34 | # print(z, n) 35 | return order("1", z, m-1, n) 36 | 37 | a = ans(n,m) 38 | 39 | for i in range(len(a)): 40 | if i % 6 == 0 and i > 0: print(" ", end="") 41 | print(a[i], end="") 42 | print() 43 | 44 | """ 45 | b) 10 46 | 0 47 | 11111 48 | 111010 001100 00 49 | 110100011000011111000 50 | 51 | c) 62 52 | 53 | d) At most, m digits can change at once. When m is odd, moving from the final number containing (m-1)/2 ones to the next 54 | number changes all m digits. Similarly, when m is even, moving from the final number containing m/2 ones to the next number 55 | also changes all m digits. 56 | """ 57 | 58 | # pass a = 1000001, b = 11000001 for part c 59 | def b(n, m, A=-1, B=-1): 60 | if n == 1: return "0" 61 | ones = 1 62 | i = 1 63 | c = 1 64 | 65 | while True: 66 | a = ans(i, ones) 67 | if a == A: 68 | start = c 69 | if a == B: 70 | return c - start - 1 71 | if len(a) <= m: 72 | c += 1 73 | i += 1 74 | else: 75 | ones += 1 76 | i = 1 77 | # print(ones) 78 | if c == n: 79 | return a 80 | 81 | #print(b(n,m,"1000001", "11000001")) 82 | -------------------------------------------------------------------------------- /2001/2001 Q3 - A Space Oddity.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import time as TIME 3 | 4 | n = int(input()) 5 | astronauts = list(map(int, input().split())) 6 | 7 | start = TIME.time() 8 | 9 | alpha = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 10 | 11 | modes = defaultdict(int) 12 | speeds = {alpha[i]:astronauts[i] for i in range(n)} 13 | astro = set(alpha[:n]) 14 | 15 | pending = [(n, 0, astro, set())] 16 | while len(pending) > 0: 17 | pending = sorted(pending) 18 | curr = pending.pop(0) 19 | # print(curr, pending) 20 | for a1 in curr[2]: 21 | for a2 in curr[2]: 22 | if a1 < a2: 23 | time = max(speeds[a1], speeds[a2]) 24 | time += curr[1] 25 | remaining = set(curr[2]) 26 | crossed = set(curr[3]) 27 | remaining.remove(a1) 28 | remaining.remove(a2) 29 | crossed.add(a1) 30 | crossed.add(a2) 31 | fastest = float("inf") 32 | f = "A" 33 | for a in crossed: 34 | if speeds[a] < fastest: 35 | fastest = speeds[a] 36 | f = a 37 | crossed.remove(f) 38 | remaining.add(f) 39 | time += speeds[f] 40 | 41 | if (len(remaining)) == 1: 42 | final = list(remaining)[0] 43 | time -= speeds[final] 44 | key = frozenset() 45 | if modes[key] == 0 or time < modes[key]: 46 | modes[key] = time 47 | # print(time) 48 | else: 49 | key = frozenset(remaining) 50 | if modes[key] == 0 or time < modes[key]: 51 | modes[key] = time 52 | pending.append((len(remaining), time, remaining, crossed)) 53 | 54 | print(modes[frozenset()]) 55 | print("Time:", (TIME.time() - start)) 56 | 57 | """ 58 | B) AB (2) cross then A (1) crosses back. CD (5) cross then B (2) crosses back. Finally AB cross (2) = 12 min 59 | """ 60 | -------------------------------------------------------------------------------- /2018/2018 Q3 - Serial Numbers.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict 2 | import time 3 | 4 | def allSwap(n): 5 | ans = set() 6 | for i in range(len(n) - 1): 7 | a = int(n[i]) 8 | b = int(n[i+1]) 9 | v = False 10 | if i > 0: 11 | c = int(n[i-1]) 12 | if c > min(a,b) and c < max(a,b): v = True 13 | if i < len(n) - 2: 14 | c = int(n[i+2]) 15 | if c > min(a,b) and c < max(a,b): v = True 16 | if v: 17 | m = list(n) 18 | m[i] = str(b) 19 | m[i+1] = str(a) 20 | ans.add("".join(m)) 21 | return ans 22 | 23 | n = int(input()) 24 | a = input() 25 | 26 | start = time.time() 27 | 28 | graph = defaultdict(set) 29 | 30 | # create graph 31 | def createGraph(a): 32 | global graph 33 | graph = defaultdict(set) 34 | seen = set() 35 | pending = deque([a]) 36 | while pending: 37 | item = pending.popleft() 38 | w = allSwap(item) 39 | for order in w: 40 | graph[item].add(order) 41 | graph[order].add(item) 42 | if order not in seen: 43 | seen.add(order) 44 | pending.append(order) 45 | 46 | #print(graph) 47 | 48 | # find the furthest node 49 | def findFurthest(a): 50 | global graph 51 | pending = deque([(a, 0)]) 52 | furthest = 0 53 | node = "" 54 | seen = set() 55 | while pending: 56 | item, d = pending.popleft() 57 | seen.add(item) 58 | for i in graph[item]: 59 | if i not in seen: 60 | seen.add(i) 61 | pending.append((i, d+1)) 62 | if d > furthest: 63 | furthest = d 64 | node = item 65 | return (node, d, seen) 66 | 67 | createGraph(a) 68 | node, d, seen = findFurthest(a) 69 | #print(d) 70 | #nodeEnd, d = findFurthest(node) 71 | 72 | print(d) 73 | #print(node, nodeEnd) 74 | 75 | 76 | 77 | print("Time:", time.time() - start) 78 | 79 | """ 80 | b) 7 : 324165-326451 81 | 16 : 865312479-136897542 82 | 83 | c) 84 | """ 85 | -------------------------------------------------------------------------------- /2024/2024 - Q1 Integer Strings.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | from time import time 3 | 4 | def count_len_below_2(n, x): 5 | return (n)*x 6 | 7 | def count_len_below(n): 8 | if n < 10: return n 9 | i = 1 10 | l = 1 11 | ans = 0 12 | while i*10 <= n: 13 | ans += 9*(i)*(l) 14 | i *= 10 15 | l += 1 16 | # print(i, l, n, ans) 17 | ans += ((n//i)-1)*(i)*(l) 18 | # print(ans) 19 | ans += count_len_below_2(n%i+1, len(str(n))) 20 | return int(ans) 21 | 22 | def count_brute(n): 23 | string = "" 24 | for i in range(1, n): 25 | string += str(i) 26 | return len(string) 27 | 28 | def find_brute(start, n): 29 | string = "" 30 | i = start 31 | while len(string) < n: 32 | string += str(i) 33 | i += 1 34 | return string[n-1] 35 | 36 | def get_nth(i): 37 | MIN = 1 38 | MAX = int(pow(2, 64)) 39 | while MIN < MAX: 40 | HALF = (MAX + MIN) // 2 41 | # print(MIN, MAX, HALF) 42 | if i > count_len_below(HALF): 43 | MIN = HALF + 1 44 | else: 45 | MAX = HALF 46 | i -= count_len_below(MIN-1) + 1 47 | return str(MIN)[i] 48 | 49 | n, j = list(map(int, input().split())) 50 | start = time() 51 | i = j+count_len_below(n-1) 52 | print(get_nth(i)) 53 | print("Time taken:", time() - start) 54 | #print(find_brute(n, j)) 55 | 56 | """ 57 | b) 12 58 | c) 11111 occurs first at 110 [111 11]2 59 | There are 222 digits to the left of 111, so the substring first occurs between 223-227 60 | 61 | 987654321 occurs first at 1[98765432 1]987654323 62 | There are 1677777777 digits to the left of 198765432, so the substring first occurs between 1677777779 and 1677777787 63 | """ 64 | 65 | def b(length, digit): 66 | i = 1 67 | s = "" 68 | while len(s) < length: 69 | s += str(i) 70 | i += 1 71 | s = s[:length] 72 | return s.count(str(digit)) 73 | 74 | #print(b(101, 5)) 75 | 76 | def c(substring): 77 | substring = str(substring) 78 | s = "" 79 | i = 1 80 | while substring not in s: 81 | s += str(i) 82 | i += 1 83 | return s.index(substring) 84 | 85 | #print(c(11111)) 86 | 87 | -------------------------------------------------------------------------------- /2000/2000 Q2 - Ants.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | board = [["." for _ in range(11)] for _ in range(11)] 4 | 5 | clockwise = {"T":"R", "R":"B", "B":"L", "L":"T"} 6 | anticlock = {"T":"L", "L":"B", "B":"R", "R":"T"} 7 | directions = {"T":(0,-1), "R":(1,0), "B":(0,1), "L":(-1,0)} 8 | flip = {"*":".", ".":"*"} 9 | 10 | def convertToBoard(x,y): 11 | x -= 1 12 | y = 11 - y 13 | return x,y 14 | 15 | def convertToScreen(x,y): 16 | x += 1 17 | y = 11 - y 18 | return x,y 19 | 20 | ant1 = input().split() 21 | x,y = convertToBoard(int(ant1[0]), int(ant1[1])) 22 | ant1 = (x,y,ant1[2]) 23 | 24 | ant2 = input().split() 25 | x,y = convertToBoard(int(ant2[0]), int(ant2[1])) 26 | ant2 = (x,y,ant2[2]) 27 | removed1 = False 28 | removed2 = False 29 | 30 | 31 | 32 | def drawBoard(): 33 | for line in board: 34 | print("".join(line)) 35 | 36 | def outside(x,y): 37 | return x < 0 or y < 0 or x >= 11 or y >= 11 38 | 39 | def move(ant): 40 | global board 41 | x,y,d = ant 42 | dir = directions[d] 43 | 44 | # move 45 | x += dir[0] 46 | y += dir[1] 47 | if outside(x,y): return ant, True 48 | 49 | # rotate 50 | if board[y][x] == "*": d = anticlock[d] 51 | else: d = clockwise[d] 52 | 53 | # change colour 54 | board[y][x] = flip[board[y][x]] 55 | 56 | # return ant 57 | return (x,y,d), False 58 | 59 | 60 | 61 | while True: 62 | n = int(input()) 63 | if n == -1: break 64 | for i in range(n): 65 | if not removed1: 66 | ant1, removed1 = move(ant1) 67 | if not removed2: 68 | ant2, removed2 = move(ant2) 69 | drawBoard() 70 | if removed1: print("Removed") 71 | else: 72 | x,y = convertToScreen(ant1[0], ant1[1]) 73 | print(x,y, ant1[2]) 74 | if removed2: print("Removed") 75 | else: 76 | x,y = convertToScreen(ant2[0], ant2[1]) 77 | print(x,y, ant2[2]) 78 | 79 | """ 80 | b) 81 | ..... 82 | ..*.. 83 | .*... 84 | .*... 85 | ..... 86 | ..*.. 87 | 3 4 T 88 | 3 6 B 89 | 90 | c) No. Ants only turn 90* clockwise or anticlockwise every turn, so they alternate facing horizontally and vertically. 91 | After an even number of turns, the ant must still be facing horizontally. The ant can only return to its starting square 92 | after an even number of turns and so it must still be facing horizontally. 93 | """ -------------------------------------------------------------------------------- /2009/2009 Q3 - Child's Play.py: -------------------------------------------------------------------------------- 1 | import time 2 | """ 3 | 4 | seen = set() 5 | def func(items): 6 | if len(items) == 1: return 7 | for i in range(len(items)): 8 | for j in range(len(items)): 9 | if i < j: 10 | for a in range(len(items) - 1): 11 | q = list(items) 12 | A = q[i] 13 | B = q[j] 14 | if A+B < 10: 15 | q.remove(A) 16 | q.remove(B) 17 | q.insert(a, A+B) 18 | if tuple(q) not in seen: 19 | seen.add(tuple(q)) 20 | func(q) 21 | 22 | w = [1 for i in range(int(input()))] 23 | 24 | func(w) 25 | seen.add(tuple(w)) 26 | #print(seen) 27 | print(len(seen)) 28 | order = sorted(list(seen)) 29 | print(order[int(input()) - 1]) 30 | print(order.index((2,1,1,2,1,1))) 31 | """ 32 | 33 | def getTot(n): 34 | if n < 10: 35 | return max(1,2**(n-1)) 36 | n -= 10 37 | s = 2**9 - 1 38 | c = -1 39 | k = 1 40 | while n > 0: 41 | n -= 1 42 | c += 1 43 | s *= 2 44 | s -= getTot(k) 45 | k += 1 46 | return s 47 | 48 | q, p = list(map(int, input().split())) 49 | 50 | start= time.time() 51 | 52 | #print(getTot(q)) 53 | 54 | o = q 55 | 56 | Q = q 57 | ans = [] 58 | curr = 1 59 | while p > 0 and sum(ans) < o: 60 | # print(curr, p, q, getTot(q-1)) 61 | if p <= getTot(q-1): 62 | ans.append(curr) 63 | q = o - sum(ans) 64 | # Q -= 1 65 | curr = 1 66 | else: 67 | p -= getTot(q-1) 68 | q -= 1 69 | curr += 1 70 | 71 | for item in ans: print(item, end=" ") 72 | print() 73 | print("Time:", time.time() - start) 74 | 75 | """ 76 | b) 88 blocks 77 | 32 * 1, 16 * 2, 10 * 3, 8 * 4, 6 * 5, 5 * 6, 4 * 7, 4 * 8, 3 * 9 78 | 32 + 16 + 10 + 8 + 6 + 5 + 4 + 4 + 3 = 88 79 | 80 | c) They are the reverse of the other. The last entry in the dictionary starts with as many 9s as possible. If there is a 81 | digit that it contains that is not a 9 at is at the end. The first entry in the numeric order must be as small (short) as 82 | possible. The number with the most nines is also the shortest number. If there is a digit that it contains that is not a 83 | 9 at is at the start. 84 | 85 | d) 86 | 87 | """ -------------------------------------------------------------------------------- /2002/2002 Q2 - Shuffling.py: -------------------------------------------------------------------------------- 1 | import time 2 | data = input() 3 | start = time.time() 4 | 5 | def expand(N): 6 | ans = "" 7 | n = list(N) 8 | while len(n) > 0: 9 | look = n.pop(0) 10 | if look in list("bio"): ans += look 11 | else: 12 | look = int(look) 13 | b = 0 14 | c = 0 15 | info = "" 16 | while b > 0 or c == 0: 17 | info += n[c] 18 | if n[c] == ")": b -= 1 19 | elif n[c] == "(": b += 1 20 | c += 1 21 | if info[0] == "(": 22 | info = info[1:-1] 23 | ans += look * info 24 | n = n[c:] 25 | 26 | return ans 27 | 28 | def expandFully(data): 29 | while True: 30 | new = expand(data) 31 | if new == data: return data 32 | data = new 33 | 34 | data = expandFully(data) 35 | #print(data) 36 | 37 | end = 8 38 | cards = [i+1 for i in range(end)] 39 | 40 | for item in data: 41 | if item == "b": 42 | c = cards.pop(0) 43 | cards.append(c) 44 | else: 45 | a = cards[:end//2] 46 | b = cards[end//2:] 47 | d = [] 48 | if item == "o": 49 | for i in range(end//2): 50 | d.append(a[i]) 51 | d.append(b[i]) 52 | else: 53 | for i in range(end//2): 54 | d.append(b[i]) 55 | d.append(a[i]) 56 | cards = list(d) 57 | # print(cards) 58 | 59 | for item in cards: print(item, end=" ") 60 | print() 61 | print("Time:", time.time() - start) 62 | 63 | """ 64 | b) 12 17 2 7 13 18 3 8 14 19 4 9 15 20 5 10 16 1 6 11 65 | c) 8 breaks, 6 in shuffles, 3 out shuffles 66 | d) Yes, it is always possible to restore a deck to its original state using only in riffles 67 | For an even sized deck, all cards change position, for an odd sized deck the last card never moves 68 | The in shuffle always changes the ordering, and there are only a finite number of possible orderings, 69 | thus the deck must always return to its original state. 70 | Each in riffle shuffle on a unique deck gives creates a unique deck and so each ordering can only be created by 71 | riffle shuffling a certain unique ordering. Therefore, a circular loop of orderings is created and the first ordering 72 | that is returned to is the original state. 73 | """ -------------------------------------------------------------------------------- /2021/2021 Q3 - Window Dressing.py: -------------------------------------------------------------------------------- 1 | from _collections import deque, defaultdict 2 | from time import time 3 | 4 | shortest = dict() 5 | parent = dict() 6 | 7 | def q3(t, c=False): 8 | if t == "A": return 1 9 | letters = list("ABCDEFGHIJKL") 10 | pending = deque([["A", 1]]) 11 | best = 999999 12 | while pending: 13 | # print(pending) 14 | look, d = pending.popleft() 15 | # print(look) 16 | if d > best + 5: continue 17 | # add a letter 18 | if len(look) < len(t): 19 | i = letters[letters.index(max(look))+1] 20 | new = look + i 21 | if new not in shortest.keys() or d+1 < shortest[new]: 22 | shortest[new] = d+1 23 | parent[new] = [look] 24 | pending.append([new, d+1]) 25 | elif d+1 == shortest[new]: 26 | parent[new].append(look) 27 | if new == t: 28 | if not c: return d+1 29 | else: best = d+1 30 | 31 | # swap 32 | if len(look) > 1: 33 | new = look[1] + look[0] + look[2:] 34 | if new not in shortest.keys() or d+1 < shortest[new]: 35 | shortest[new] = d+1 36 | parent[new] = [look] 37 | pending.append([new, d+1]) 38 | elif d+1 == shortest[new]: 39 | parent[new].append(look) 40 | if new == t: 41 | if not c: return d+1 42 | else: best = d+1 43 | 44 | # rotate 45 | if len(look) > 1: 46 | new = look[1:] + look[0] 47 | if new not in shortest.keys() or d+1 < shortest[new]: 48 | shortest[new] = d+1 49 | parent[new] = [look] 50 | pending.append([new, d+1]) 51 | elif d+1 == shortest[new]: 52 | parent[new].append(look) 53 | if new == t: 54 | if not c: return d+1 55 | else: best = d+1 56 | 57 | return best 58 | 59 | def countWays(state): 60 | ways = 0 61 | if state not in parent: return 1 62 | for prev in parent[state]: 63 | ways += countWays(prev) 64 | 65 | return ways 66 | 67 | def c(end): 68 | print(countWays(end)) 69 | 70 | a = input() 71 | start = time() 72 | print(q3(a, True)) 73 | 74 | c(a) 75 | #print(time() - start) -------------------------------------------------------------------------------- /2025/2025 Q1 - Palidromic Sums.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | 3 | def is_palindrome(n): 4 | return str(n) == str(n)[::-1] 5 | 6 | def next_palindrome(n): 7 | x = n 8 | if not is_palindrome(x): 9 | if len(str(x)) % 2 == 1: 10 | x = str(x) 11 | x = x[:len(x)//2+1] 12 | y = x[:-1] 13 | x = int(x + y[::-1]) 14 | else: 15 | x = str(x) 16 | x = x[:len(x)//2] 17 | x = int(x + x[::-1]) 18 | if str(x).count("9") == len(str(x)): 19 | return next_palindrome(x+1) 20 | 21 | while x <= n: 22 | x = increment_palindrome(x) 23 | return x 24 | 25 | def increment_palindrome(x): 26 | # increment middle number 27 | x = list(str(x)) 28 | i = (len(x)-1)//2 29 | while True: 30 | if x[i] != "9": 31 | inc = str(int(x[i])+1) 32 | x[i] = x[len(x)-1-i] = inc 33 | return int("".join(x)) 34 | else: 35 | x[i] = x[len(x)-1-i] = "0" 36 | i -= 1 37 | 38 | n = int(input()) 39 | start = time() 40 | 41 | palindromes = [] 42 | i = 1 43 | while i <= n: 44 | palindromes.append(i) 45 | i = next_palindrome(i) 46 | print(len(palindromes)) 47 | print(time() - start) 48 | 49 | def find_sum(n, length, d=1): 50 | if length <= 0: 51 | return False 52 | if n <= 0: 53 | return False 54 | if is_palindrome(n): 55 | return [n] 56 | 57 | i = 0 58 | if d == -1: i = len(palindromes)-1 59 | for _ in range(len(palindromes)): 60 | x = find_sum(n-palindromes[i], length-1, -1) 61 | if x: return [palindromes[i]] + x 62 | i += d 63 | 64 | return False 65 | 66 | def palindromic_sum(n): 67 | if is_palindrome(n): return [n] 68 | x = find_sum(n, 2) 69 | if x: return x 70 | return find_sum(n, 3) 71 | 72 | sum = palindromic_sum(n) 73 | sum = sorted(sum) 74 | for i in sum: print(i, end=" ") 75 | print() 76 | print(time() - start) 77 | print() 78 | 79 | def c(max_number): 80 | p = set() 81 | palindromes = [] 82 | i = 1 83 | while i <= max_number: 84 | palindromes.append(i) 85 | p.add(i) 86 | i = next_palindrome(i) 87 | for a in palindromes: 88 | for b in palindromes: 89 | if a+b <= max_number: 90 | p.add(a+b) 91 | print(max_number - len(p)) 92 | 93 | c(1000000) 94 | 95 | """ 96 | b) 97 | 1 9 44 98 | 2 8 44 99 | 3 7 44 100 | 4 6 44 101 | 5 5 44 102 | 103 | c) 266948 104 | """ 105 | -------------------------------------------------------------------------------- /2015/2015 Q3 - Modern Art.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | import time 3 | 4 | a,b,c,d,n = list(map(int, input().split())) 5 | 6 | start = time.time() 7 | 8 | @lru_cache(maxsize=None) 9 | def factorial(n): 10 | t = 1 11 | while n > 1: 12 | t *= n 13 | n -= 1 14 | return t 15 | 16 | @lru_cache(maxsize=None) 17 | def perms(a,b,c,d): 18 | top = factorial(a+b+c+d) 19 | bottom = factorial(a) * factorial(b) * factorial(c) * factorial(d) 20 | return top / bottom 21 | 22 | letters = "ABCD" 23 | 24 | def order(word, pos, n): 25 | # print(word, pos, n) 26 | if sum(pos) == 1: 27 | for i in range(len(pos)): 28 | if pos[i] == 1: 29 | return word + letters[i] 30 | 31 | # check if A 32 | if pos[0] > 0: 33 | if n <= perms(pos[0] - 1, pos[1], pos[2], pos[3]): 34 | return order(word + "A", [pos[0] - 1, pos[1], pos[2], pos[3]], n) 35 | else: 36 | n -= perms(pos[0] - 1, pos[1], pos[2], pos[3]) 37 | # check if B 38 | if pos[1] > 0: 39 | if n <= perms(pos[0], pos[1] - 1, pos[2], pos[3]): 40 | return order(word + "B", [pos[0], pos[1] - 1, pos[2], pos[3]], n) 41 | else: 42 | n -= perms(pos[0], pos[1] - 1, pos[2], pos[3]) 43 | # check if C 44 | if pos[2] > 0: 45 | if n <= perms(pos[0], pos[1], pos[2] - 1, pos[3]): 46 | return order(word + "C", [pos[0], pos[1], pos[2] - 1, pos[3]], n) 47 | else: 48 | n -= perms(pos[0], pos[1], pos[2] - 1, pos[3]) 49 | # check if D 50 | if pos[3] > 0: 51 | if n <= perms(pos[0] , pos[1], pos[2], pos[3] - 1): 52 | return order(word + "D", [pos[0], pos[1], pos[2], pos[3] - 1], n) 53 | else: 54 | n -= perms(pos[0], pos[1], pos[2], pos[3] - 1) 55 | 56 | #print(factorial(0)) 57 | print(order("", [a,b,c,d], n)) 58 | print("Time:", time.time() - start) 59 | 60 | """ 61 | b) 10 62 | 63 | c) No. If every position has changed then the left-most position must have moved, meaning that in the nth position, 64 | the other positions where in reverse alphabetical order. Since the left-most position has changed, in the n+1st position, 65 | all of the other positions are now in alphabetical order. The only way for all positions to change at once is if they are 66 | in reverse alphabetical order. The only way for an order to be both alphabetical and reverse alphabetical is if there 67 | is only 1 artist. If there was only 1 artist (ignoring the left-most position) there would be no n+2nd position after the 68 | n+1st position. 69 | """ 70 | -------------------------------------------------------------------------------- /2016/2016 Q2 - Migration.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import time 3 | 4 | p, s, n = list(map(int, input().split())) 5 | p -= 1 6 | seq = list(map(int, input().split())) 7 | 8 | start = time.time() 9 | 10 | places = defaultdict(int) 11 | 12 | def pToPos(a): 13 | y = a // 5 14 | x = a % 5 15 | return (x,y) 16 | 17 | def draw(): 18 | for y in range(5): 19 | for x in range(0,5): 20 | print(places[(x,y)],end="") 21 | print() 22 | print() 23 | 24 | for i in range(n): 25 | places[pToPos(p)] += 1 26 | 27 | while True: 28 | handled = True 29 | for item in list(places.keys()): 30 | while places[item] >= 4: 31 | places[item] -= 4 32 | places[(item[0]-1, item[1])] += 1 # left 33 | places[(item[0]+1, item[1])] += 1 # right 34 | places[(item[0], item[1]-1)] += 1 # up 35 | places[(item[0], item[1]+1)] += 1 # down 36 | handled = False 37 | if handled: 38 | break 39 | 40 | p = (p + seq[i % len(seq)]) % 25 41 | 42 | draw() 43 | 44 | print("Time:", time.time() - start) 45 | 46 | """ 47 | b) 4 * 4 = 16 people 48 | c) 20 49 | Example: 50 | 21 3 8 51 | 12 18 5 52 | 53 | Code is below 54 | 55 | d) No. Some ending landscapes can be created by multiple different starting landscapes. If a migration occurred 56 | from the position and the there is still someone remaining at that position then it is also possible that a migration 57 | didn't occur and that the person was just placed there. For example: 58 | 59 | 0 0 0 0 0 1 0 0 60 | 0 3 0 0 1 0 1 0 61 | 0[3]0 0 -> 1 1 1 0 62 | 0 0 0 0 0 1 0 0 63 | 64 | but also 65 | 66 | 0 1 0 0 0 1 0 0 67 | 1 0 1 0 1 0 1 0 68 | 1[0]1 0 -> 1 1 1 0 69 | 0 1 0 0 0 1 0 0 70 | """ 71 | 72 | """ 73 | Code for part C 74 | 75 | from _collections import defaultdict 76 | 77 | ans = "1010010100100001010010000" 78 | 79 | for i in range(25): 80 | print(i) 81 | for a in range(24): 82 | for b in range(24): 83 | for c in range(24): 84 | seq = [a,b,c] 85 | p = i - 1 86 | places = defaultdict(int) 87 | for k in range(8): 88 | places[p] += 1 89 | p = (p + seq[k % len(seq)]) % 25 90 | 91 | n = "" 92 | for k in range(25): 93 | n += str(places[k]) 94 | if n == ans: 95 | print(i, 3, 8) 96 | print(a, b, c) 97 | print() 98 | """ 99 | -------------------------------------------------------------------------------- /2007/2007 Q3 - String Rewriting.py: -------------------------------------------------------------------------------- 1 | import time 2 | import math 3 | 4 | word = input() 5 | s, p = list(map(int, input().split())) 6 | 7 | start = time.time() 8 | 9 | def wordToList(word): 10 | return [word.count("A"), word.count("B"), word.count("C"), word.count("D"), word.count("E")] 11 | 12 | 13 | def combine(a,b): 14 | c = [] 15 | for i in range(len(a)): 16 | c.append(a[i] + b[i]) 17 | return c 18 | 19 | 20 | def rewrite(word=str, n=1): 21 | for i in range(n): 22 | word = word.replace("E", "EE").replace("C", "Cd").replace("D", "DC").replace("B", "aB").replace("A", "B").replace("a", "A").replace("d", "D") 23 | return word 24 | 25 | 26 | def getLen(word, n): 27 | for i in range(n): 28 | word = nextLen(word) 29 | return word 30 | 31 | def nextLen(word): 32 | a = word[1] 33 | b = word[0] + word[1] 34 | c = word[2] + word[3] 35 | d = word[2] + word[3] 36 | e = word[4] * 2 37 | return [a,b,c,d,e] 38 | 39 | 40 | def func(word, count, s, p, c): 41 | # print(c) 42 | # print(word, count, s) 43 | if s == 0: 44 | # print(word) 45 | # print(p-sum(count)) 46 | return combine(count, wordToList(word[:p-sum(count)])) 47 | tot = sum(getLen(count, s)) 48 | for i in range(1, len(word)+1): 49 | curr = getLen(wordToList(word[:i]), s) 50 | if tot + sum(curr) >= p: 51 | # print("curr", curr) 52 | # all this can be handled numerically 53 | i -= 1 54 | # print(word[i:i+c], i+c, i) 55 | return func(rewrite(word[i:i+c], 1), combine(getLen(count, 1), getLen(wordToList(word[:i]), 1)), s-1, p, c+1) 56 | return combine(count,getLen(wordToList(word), s)) 57 | 58 | ans = func(word, [0,0,0,0,0], s, p, 2) 59 | for item in ans: 60 | print(item, end=" ") 61 | print() 62 | 63 | print("Time:", time.time() - start) 64 | 65 | """ 66 | b) ABBABBABABBABBABABBABABBABBABABBAB, 34 (starting A) 67 | 256 (starting C) 68 | CDDCDCCDDCCDCDDCDCCDCDDCCDDCDCCDDCCDCDDCCDDCDCCDCDDCDCCDDCCDCDDCDCCDCDDCCDDCDCCDCDDCDCCDDCCDCDDCCDDCDCCDDCCDCDDCDCCDCDDCCDDCDCCDDCCDCDDCCDDCDCCDCDDCDCCDDCCDCDDCCDDCDCCDDCCDCDDCDCCDCDDCCDDCDCCDCDDCDCCDDCCDCDDCDCCDCDDCCDDCDCCDDCCDCDDCCDDCDCCDCDDCDCCDDCCDCDDC 69 | 70 | c) No. Every C is generated from either a D or a C in the previous string. After each rewriting, every C is replaced with 71 | a C followed by a D and every D is replaced with a D followed by a C. Thus, every C must be adjacent to a D (either before 72 | or after). If there are 2 Cs next to each other then the first C must follow a D and the second C must be followed by a D 73 | So there can never be more than 2 Cs next to each other. 74 | 75 | d) A -> AA 76 | B -> A 77 | """ 78 | 79 | 80 | -------------------------------------------------------------------------------- /1996/1996 Q2 - Life.py: -------------------------------------------------------------------------------- 1 | alive = set() 2 | dead = set() 3 | for y in range(11): 4 | for x in range(11): 5 | dead.add((x,y)) 6 | 7 | def getNumNeighbours(pos, alive): 8 | directions = [(-1, -1), (-1, 0), (-1, 1), (0, 1), (0, -1), (1, 1), (1, 0), (1, -1)] 9 | neighbours = 0 10 | for d in directions: 11 | if (pos[0] + d[0], pos[1] + d[1]) in alive: neighbours += 1 12 | return neighbours 13 | 14 | def nextGeneration(alive, dead, n): 15 | if n == 0: return alive 16 | next = set() 17 | nextD = set() 18 | for item in alive: 19 | num = getNumNeighbours(item, alive) 20 | if num in {2,3}: 21 | # lives 22 | next.add(item) 23 | else: 24 | nextD.add(item) 25 | for item in dead: 26 | num = getNumNeighbours(item, alive) 27 | if num == 3: 28 | # birth 29 | next.add(item) 30 | else: 31 | nextD.add(item) 32 | 33 | return nextGeneration(next, nextD, n-1) 34 | 35 | def output(alive): 36 | for y in range(11): 37 | for x in range(11): 38 | if (x,y) in alive: 39 | print("0", end="") 40 | else: 41 | print(".", end="") 42 | print() 43 | 44 | def setup(): 45 | global alive, dead 46 | alive = set() 47 | dead = set() 48 | for y in range(11): 49 | for x in range(11): 50 | dead.add((x, y)) 51 | 52 | for y in range(5): 53 | line = list(input()) 54 | for x in range(len(line)): 55 | if line[x] == "0": 56 | pos = (x+3, y+3) 57 | dead.remove(pos) 58 | alive.add(pos) 59 | 60 | curr = 0 61 | setup() 62 | output(alive) 63 | while True: 64 | val = input() 65 | a = set(alive) 66 | if val == "-1": break 67 | if val[0] == "#": 68 | n = int(val[1:]) 69 | a = nextGeneration(a, set(dead), n) 70 | curr = n 71 | if val[0] == "+": 72 | n = int(val[1:]) 73 | curr += n 74 | a = nextGeneration(a, set(dead), curr) 75 | 76 | output(a) 77 | 78 | """ 79 | b) It is not feasible to have an -n function because several different boards can have identical future generations, 80 | thus there may not be a unique preceding generation to the current one. Furthermore, there might not even be a preceding 81 | generation, for example, if the entire board is full. 82 | 83 | c) 262,144 84 | Note that survival and death rules are the same, and that survival and birth are the same, but with a different centre. 85 | As there are 9 cells that affect the current cell (including the cell itself), there are 2^9 birth conditions and 86 | 2^9 survival conditions, giving 512 * 512 conditions 87 | 88 | d) 89 | """ -------------------------------------------------------------------------------- /2025/2025 Q3 - Fuses.py: -------------------------------------------------------------------------------- 1 | from collections import * 2 | from cfractions import Fraction 3 | from time import time 4 | from itertools import product 5 | 6 | 7 | def get_time(fuse): 8 | if fuse[1] == 0: return float("inf") 9 | return fuse[0] / fuse[1] 10 | 11 | def get_smallest_time(fuses): 12 | t = float("inf") 13 | for fuse in fuses: 14 | t = min(t, get_time(fuse)) 15 | return t 16 | 17 | def count_ways(fuses): 18 | times = {0} 19 | q = deque() 20 | start = [] 21 | for f in fuses: start.append((f, 0)) 22 | start = tuple(start) 23 | state = (start, 0) 24 | seen = set() 25 | seen.add(state) 26 | q.append(state) 27 | c = 0 28 | 29 | while q: 30 | fuses, t = q.pop() 31 | times.add(t) 32 | c += 1 33 | 34 | for state in product([0,1,2], repeat=len(fuses)): 35 | if sum(state) == 0: continue 36 | new_fuses = [] 37 | for f in range(len(fuses)): 38 | new_fuses.append((fuses[f][0], max(state[f], fuses[f][1]))) 39 | # if new_fuses == fuses: continue 40 | dt = get_smallest_time(new_fuses) 41 | if dt == float("inf"): 42 | continue 43 | n_fuses = [] 44 | for j in range(len(new_fuses)): 45 | fuse = (new_fuses[j][0] - dt * new_fuses[j][1], new_fuses[j][1]) 46 | if fuse[0] > 0: 47 | n_fuses.append(fuse) 48 | n_fuses = tuple(sorted(n_fuses)) 49 | 50 | state = (n_fuses, 0) 51 | if state not in seen: 52 | seen.add(state) 53 | q.append(state) 54 | state = (n_fuses, t+dt) 55 | if state not in seen: 56 | seen.add(state) 57 | q.append(state) 58 | # print(sorted(list(times))) 59 | # print(c) 60 | return len(times) 61 | 62 | fuses = list(map(int, input().split())) 63 | fuses.pop(0) 64 | start_time = time() 65 | #for i in range(len(fuses)): 66 | # fuses[i] = Fraction(fuses[i], 1) 67 | print(count_ways(fuses)) 68 | print(time() - start_time) 69 | 70 | def c(MAX): 71 | ans = 0 72 | for a in range(1, MAX+1): 73 | print(a, ans) 74 | for b in range(1, a+1): 75 | ans = max(ans, count_ways([Fraction(a, 1), Fraction(b, 1)])) 76 | print("For 2 fuses:", ans) 77 | 78 | ans = 0 79 | for a in range(1, MAX + 1): 80 | print(a, ans) 81 | for b in range(1, a + 1): 82 | for c in range(1, b + 1): 83 | ans = max(ans, count_ways([Fraction(a, 1), Fraction(b, 1), Fraction(c, 1)])) 84 | print("For 3 fuses:", ans) 85 | 86 | #c(100) 87 | 88 | 89 | """ 90 | b) 0, 1, 2, 3, 0.5, 2.5, 1.5, 0.75, 1.25 91 | 92 | c) For 2: 17, For 3: 163 93 | """ -------------------------------------------------------------------------------- /2010/2010 Q3 - Juggl(ug)ling.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | import time 3 | startTime = 0 4 | 5 | """ 6 | NOTE THAT THIS SOLUTION IS A BIT TOO SLOW FOR SOME TEST CASES 7 | """ 8 | 9 | def juggling(): 10 | global startTime 11 | j, n = list(map(int, input().split())) 12 | jugs = list(map(int, input().split())) 13 | 14 | startTime = time.time() 15 | 16 | start = tuple([0 for i in range(j)]) 17 | 18 | pending = deque([(start, 0)]) 19 | 20 | seen = set() 21 | 22 | while pending: 23 | check = pending.popleft() 24 | # print(check, pending) 25 | item, d = check 26 | 27 | # we can either fill, move or empty 28 | # fill jugs 29 | for i in range(j): 30 | this = list(item) 31 | this[i] = jugs[i] 32 | this = tuple(this) 33 | if n in this: 34 | print(d + 1) 35 | return 36 | if this not in seen: 37 | seen.add(this) 38 | pending.append((this, d+1)) 39 | # empty jugs 40 | for i in range(j): 41 | if item[i] > 0: 42 | this = list(item) 43 | this[i] = 0 44 | this = tuple(this) 45 | if n in this: 46 | print(d + 1) 47 | return 48 | if this not in seen: 49 | seen.add(this) 50 | pending.append((this, d+1)) 51 | # pour jugs 52 | for a in range(j): 53 | for b in range(j): 54 | if a != b and item[a] > 0: 55 | this = list(item) 56 | diff = min(jugs[b] - this[b], this[a]) 57 | this[b] = min(jugs[b], this[a] + this[b]) 58 | this[a] = max(0, this[a] - diff) 59 | this = tuple(this) 60 | if n in this: 61 | print(d + 1) 62 | return 63 | if this not in seen: 64 | seen.add(this) 65 | pending.append((this, d + 1)) 66 | 67 | 68 | juggling() 69 | print("Time:", time.time() - startTime) 70 | 71 | """ 72 | b) 1. Fill B. 73 | 2. Pour B into A. 74 | 3. Empty A. 75 | 4. Pour B into A. There will be 2 oz remaining in B 76 | 77 | c) 2 & 20, 4 & 20, 78 | 79 | d) No. When liquid is poured from jug A to jug B, either jug A is emptied, jug B is filled, or both. Therefore, 80 | every operation either fills or empties a jug, so after each operation that must be at least one jug which is either 81 | full or empty. If there are no 1 oz jugs then a jug containing 1 oz of liquid is not full or empty. If every jug is 82 | neither full or empty, then there is no set of steps which will achieve this outcome. 83 | """ -------------------------------------------------------------------------------- /1998/1998 Q2 - Tamworth Two.py: -------------------------------------------------------------------------------- 1 | trees = set() 2 | farmer = (0,0) 3 | fdirection = "U" 4 | pigs = (0,0) 5 | fpigs = "U" 6 | 7 | def boardToScreen(x,y): 8 | x += 1 9 | y = 10 - y 10 | return x,y 11 | 12 | def screenToBoard(x,y): 13 | x -= 1 14 | y = 10 - y 15 | return x,y 16 | 17 | def output(): 18 | for y in range(10): 19 | for x in range(10): 20 | if (x,y) in trees: 21 | print("*", end="") 22 | elif (x,y) == farmer: 23 | if (x,y) == pigs: print("+", end="") 24 | else: print("F", end="") 25 | elif (x,y) == pigs: 26 | print("P", end="") 27 | else: 28 | print(".", end="") 29 | print() 30 | print() 31 | 32 | def offboard(pos): 33 | x = pos[0] 34 | y = pos[1] 35 | return x < 0 or y < 0 or x >= 10 or y >= 10 36 | 37 | def move(x,y,d): 38 | directions = {"U":(0,-1), "L":(-1,0), "R":(1,0), "D":(0,1)} 39 | rotate = {"U":"R", "R":"D", "D":"L", "L":"U"} 40 | dir = directions[d] 41 | next = (x + dir[0], y + dir[1]) 42 | if next in trees or offboard(next): 43 | d = rotate[d] 44 | return (x, y), d 45 | return next, d 46 | 47 | x,y = list(map(int, input().split())) 48 | pigs = screenToBoard(x,y) 49 | x,y = list(map(int, input().split())) 50 | farmer = screenToBoard(x,y) 51 | 52 | output() 53 | 54 | m = 0 55 | while True: 56 | val = input() 57 | if val == "X": break 58 | 59 | v, n = val.split() 60 | n = int(n) 61 | 62 | if v == "T": 63 | for i in range(n): 64 | x,y = list(map(int, input().split())) 65 | trees.add(screenToBoard(x,y)) 66 | 67 | if v == "M": 68 | for i in range(n): 69 | m += 1 70 | 71 | # move farmer and pigs 72 | farmer, fdirection = move(farmer[0], farmer[1], fdirection) 73 | pigs, fpigs = move(pigs[0], pigs[1], fpigs) 74 | 75 | # check if pigs have been caught 76 | if farmer == pigs: 77 | x,y = boardToScreen(farmer[0], farmer[1]) 78 | print("Farmer and pigs meet on move " + str(m) + " at (" + str(x) + "," + str(y) + ")") 79 | 80 | output() 81 | 82 | """ 83 | b) 1 84 | 81 85 | 86 | c) 19 moves. 87 | Start at (1,10) and (1,9), both facing right 88 | 89 | d) Yes, we will always be able to determine whether the pigs and the farmer meet. During the simulation, if it returns 90 | to a state that has been seen before, the simulation will continue in an infinite repetition of states that have been 91 | seen before. Once all possible states have been simulated, we will know whether or not the pigs and the farmer will meet 92 | and, if so, where. As the simulation only has a finite number of states, it must eventually reach a state that has already 93 | been seen. 94 | """ 95 | 96 | -------------------------------------------------------------------------------- /1997/1997 Q3 - Eqyptian Fractions.py: -------------------------------------------------------------------------------- 1 | import time, math 2 | from fractions import Fraction 3 | from functools import lru_cache 4 | import sys 5 | 6 | """ 7 | NOTE THAT THIS SOLUTION IS FAR TOO SLOW FOR SOME TEST CASES 8 | """ 9 | 10 | sys.setrecursionlimit(10**6) 11 | 12 | top, bot = list(map(int, input().split())) 13 | 14 | start = time.time() 15 | 16 | V = Fraction(top, bot) 17 | 18 | ans = [] 19 | allAns = [] 20 | 21 | seen = {} 22 | mini = 1 23 | 24 | def combine(a,b): 25 | if a == None or b == None: return None 26 | return a + b 27 | 28 | def optimum(a,b): 29 | if a == None: return b 30 | if b == None: return a 31 | if len(a) < len(b): return a 32 | if len(b) < len(a): return b 33 | if max(a) < max(b): return a 34 | return b 35 | 36 | 37 | def greedyEgypt(frac): 38 | frac = Fraction(frac) 39 | denom = [] 40 | while frac.numerator != 0: 41 | x = math.ceil(frac.denominator / frac.numerator) 42 | denom.append(x) 43 | frac -= Fraction(1,x) 44 | return denom 45 | 46 | @lru_cache(maxsize=None) 47 | def egypt(v, i, d, maxa): 48 | global solution, shortest, largest 49 | 50 | print(v,i,d, shortest, largest) 51 | 52 | if v == Fraction(0,1): 53 | solution = True 54 | return tuple() 55 | 56 | if d > shortest: return None 57 | if i > largest: return None 58 | if v.denominator > 32000: return None 59 | if v.numerator == 1: 60 | solution = True 61 | d += 1 62 | maxa = max(maxa, v.denominator) 63 | if d <= shortest: 64 | shortest = d 65 | largest = min(maxa, largest) 66 | return (v.denominator,) 67 | if i > 32000: return None 68 | while Fraction(1,i) > v: 69 | i += 1 70 | return optimum(combine(egypt(v-Fraction(1,i), i+1, d + 1, max(i,maxa)),(i,)), egypt(v, i+1, d, maxa)) 71 | 72 | 73 | shortest = 9 74 | largest = float("inf") 75 | 76 | v = Fraction(V) 77 | solution = False 78 | print("Greedy", greedyEgypt(v)) 79 | print(egypt(v,2,0,0)) 80 | 81 | print("Time:", time.time() - start) 82 | 83 | """ 84 | b) 1/7 85 | 31/32 86 | 87 | c) There are no fractions which have unique egyptian fractions. All fractions can be written as an egyptian fraction, and 88 | every unit fraction can be written as an egyptian fraction with more than 1 fraction. e.g. 1/7 = 1/8 + 1/56. Therefore, 89 | given an egyptian fraction, an equivalent egyptian fraction can be created by expanding one its unit fractions into another 90 | egyptian fraction with multiple terms. 91 | 92 | d) 303791 93 | 94 | """ 95 | 96 | def d(): 97 | seen = set() 98 | for a in range(1,1000): 99 | for b in range(1,1000): 100 | if a < b: 101 | seen.add(Fraction(a,b)) 102 | print(len(seen)) 103 | 104 | d() -------------------------------------------------------------------------------- /2000/2000 Q3 - Dice Games.py: -------------------------------------------------------------------------------- 1 | from _collections import * 2 | from itertools import combinations_with_replacement as combs 3 | from itertools import permutations as perms 4 | from itertools import combinations 5 | from copy import * 6 | from math import * 7 | from time import time 8 | 9 | FREQ = defaultdict(int) 10 | 11 | n = int(input()) 12 | 13 | diea = tuple(sorted(list(map(int, input().split())))) 14 | dieb = tuple(sorted(list(map(int, input().split())))) 15 | 16 | start = time() 17 | 18 | m = 0 19 | mm = 20 20 | for a in diea: 21 | for b in dieb: 22 | FREQ[a+b] += 1 23 | m = max(m, a+b) 24 | mm = min(mm, a+b) 25 | 26 | m += 1 27 | #print(mm, m) 28 | 29 | # we can guarentee that one die cannot have numbers greater than 8 30 | nums = [1,2,3,4,5,6,7,8] 31 | 32 | dice = list(combs(nums, n)) 33 | 34 | #print(FREQ) 35 | 36 | possible = False 37 | for da in dice: 38 | # da = sorted(da) 39 | # da = (1,3,3,5) 40 | # print(da) 41 | if da == diea or da == dieb: 42 | continue 43 | db = [] 44 | valid = True 45 | freq = copy(FREQ) 46 | for _ in range(n): 47 | a = -1 48 | for i in range(mm, m): 49 | if freq[i] > 0: 50 | a = i 51 | break 52 | if a == -1: 53 | valid = False 54 | break 55 | db.append(a - da[0]) 56 | if db[-1] <= 0: 57 | valid = False 58 | break 59 | for i in range(n): 60 | v = db[-1] + da[i] 61 | if freq[v] > 0: 62 | freq[v] -= 1 63 | else: 64 | valid = False 65 | break 66 | 67 | if valid: 68 | for a in da: 69 | print(a, end=" ") 70 | print() 71 | for b in db: 72 | print(b, end=" ") 73 | print() 74 | possible = True 75 | break 76 | 77 | if not possible: 78 | print("Impossible") 79 | 80 | print("Time Taken:", time() - start) 81 | 82 | ########### 83 | # PART D 84 | ########### 85 | 86 | def beats(a, b): 87 | win = 0 88 | for i in a: 89 | for j in b: 90 | if i > j: 91 | win += 1 92 | return win > 4.5 93 | 94 | def d(): 95 | dice = list(perms([1,2,3,4,5,6,7,8,9], 9)) 96 | ans = set() 97 | for d in dice: 98 | # print(d) 99 | a = d[:3] 100 | b = d[3:6] 101 | c = d[6:] 102 | if 1 not in a: 103 | continue 104 | 105 | if beats(a, b) and beats(b,c) and beats(c, a): 106 | a = tuple(sorted(a)) 107 | b = tuple(sorted(b)) 108 | c = tuple(sorted(c)) 109 | ans.add((a,b,c)) 110 | for item in ans: 111 | print(item) 112 | 113 | #d() 114 | -------------------------------------------------------------------------------- /2003/2003 Q2 - Waves - Isaac Eason.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | AUTHOR: Isaac Eason 4 | GITHUB: codethat01 5 | 6 | """ 7 | 8 | import time as TIME 9 | 10 | def putchar(depth, b1, b2, x): 11 | if x == b1 or x == b2: 12 | char = "X" 13 | elif depth == 0: 14 | char = "-" 15 | elif depth > 0: 16 | char = "*" 17 | else: 18 | char = "o" 19 | print(char, end="") 20 | 21 | p = int(input()) 22 | pebbles = [] 23 | for _ in range(p): 24 | pebbles.append(list(map(int, input().split()))) 25 | b1, b2 = map(int, input().split()) 26 | gap = b2-b1-1 27 | left = [] 28 | mid = [] 29 | right = [] 30 | for pebble in pebbles: 31 | if pebble[0] < b1: 32 | left.append(pebble) 33 | elif pebble[0] > b2: 34 | right.append(pebble) 35 | else: 36 | mid.append(pebble) 37 | t = int(input()) 38 | 39 | start = TIME.time() 40 | 41 | for y in range(4, -5, -1): 42 | for x in range(-4, 5): 43 | depth = 0 44 | if x < b1: 45 | for time, value in ((t, 1), (t-2, -1)): 46 | for pebble in left: 47 | tdiff = time - pebble[2] 48 | dx, dy = abs(pebble[0]-x), abs(pebble[1]-y) 49 | dwall = b1-pebble[0] 50 | dpix = b1-x-1 51 | if dy + dx == tdiff: 52 | depth += value 53 | if dy + dpix + dwall == tdiff: 54 | depth += value 55 | elif x > b2: 56 | for time, value in ((t, 1), (t-2, -1)): 57 | for pebble in right: 58 | tdiff = time - pebble[2] 59 | dx, dy = abs(pebble[0]-x), abs(pebble[1]-y) 60 | dwall = pebble[0]-b2 61 | dpix = x-b2-1 62 | if dy + dx == tdiff: 63 | depth += value 64 | if dy + dpix + dwall == tdiff: 65 | depth += value 66 | elif b1 < x and x < b2: 67 | for time, value in ((t, 1), (t-2, -1)): 68 | for pebble in mid: 69 | tdiff = time-pebble[2]+1 70 | ldx, rdx, dy = pebble[0]-b1, b2-pebble[0], abs(pebble[1]-y) 71 | lpix, rpix = x-b1, b2-x 72 | dxreq = tdiff-dy 73 | if (dxreq-ldx-lpix) % (2*gap) == 0 and dxreq-ldx-lpix >= 0: 74 | depth += value 75 | if (dxreq-ldx-rpix+gap) % (2*gap) == 0 and dxreq-ldx-rpix+gap > 0: 76 | depth += value 77 | if (dxreq-rdx-rpix) % (2*gap) == 0 and dxreq-rdx-rpix >= 0: 78 | depth += value 79 | if (dxreq-rdx-lpix+gap) % (2*gap) == 0 and dxreq-rdx-lpix+gap > 0: 80 | depth += value 81 | if dxreq-1 == abs(pebble[0]-x): 82 | depth += value 83 | 84 | putchar(depth, b1, b2, x) 85 | print() 86 | 87 | print("Time taken:", TIME.time() - start) 88 | -------------------------------------------------------------------------------- /2001/2001 Q2 - Playfair Cipher.py: -------------------------------------------------------------------------------- 1 | left = list(input()) 2 | right = list(input()) 3 | 4 | grid1 = [] 5 | alpha = list("ABCDEFGHIJKLMNOPRSTUVWXYZ") 6 | for y in range(5): 7 | line = "" 8 | for x in range(5): 9 | letter = "4" 10 | while letter not in alpha and len(left) > 0: 11 | letter = left.pop(0) 12 | if len(left) == 0 and letter == "4": 13 | letter = alpha.pop(0) 14 | else: 15 | alpha.remove(letter) 16 | line += letter 17 | grid1.append(line) 18 | 19 | grid2 = [] 20 | alpha = list("ABCDEFGHIJKLMNOPRSTUVWXYZ") 21 | for y in range(5): 22 | line = "" 23 | for x in range(5): 24 | letter = "4" 25 | while letter not in alpha and len(right) > 0: 26 | letter = right.pop(0) 27 | if len(right) == 0 and letter == "4": 28 | letter = alpha.pop(0) 29 | else: 30 | alpha.remove(letter) 31 | line += letter 32 | grid2.insert(0, line[::-1]) 33 | 34 | for i in range(5): 35 | print(grid1[i], grid2[i]) 36 | 37 | def findline(grid, letter): 38 | for i in range(len(grid)): 39 | if letter in grid[i]: return i 40 | 41 | def getNext(grid, letter, n): 42 | for i in range(len(grid)): 43 | if letter in grid[i]: 44 | p = grid[i].index(letter) 45 | p = (p+n) % len(grid[i]) 46 | return grid[i][p] 47 | 48 | def getCol(grid, letter, n): 49 | i = 0 50 | for line in grid: 51 | if letter in line: 52 | i = line.index(letter) 53 | return grid[n][i] 54 | 55 | inp = "" 56 | while inp != "Q": 57 | inp = input() 58 | if inp == "E": 59 | word = input() 60 | output = "" 61 | if len(word) % 2 == 1: word += "X" 62 | for i in range(0, len(word), 2): 63 | a = word[i] 64 | b = word[i+1] 65 | if findline(grid1, a) == findline(grid2, b): 66 | output += getNext(grid1, a, 1) + getNext(grid2, b, 1) 67 | else: 68 | output += getCol(grid1, a, findline(grid2, b)) + getCol(grid2, b, findline(grid1, a)) 69 | 70 | print(output) 71 | 72 | elif inp == "D": 73 | word = input() 74 | output = "" 75 | for i in range(0, len(word), 2): 76 | a = word[i] 77 | b = word[i+1] 78 | if findline(grid1, a) == findline(grid2, b): 79 | output += getNext(grid1, a, -1) + getNext(grid2, b, -1) 80 | else: 81 | output += getCol(grid1, a, findline(grid2, b)) + getCol(grid2, b, findline(grid1, a)) 82 | if output[-1] == "X": output = output[:-1] 83 | print(output) 84 | 85 | """ 86 | b) 9 (there are 2 Fs) 87 | c) Each Row must be preserved in order but can be shifted there are 5 ways to achieve this shift. The rows in both 88 | squares can be shifted by any amount. The rows can be ordered in 5! ways, however the ordering has to be the same in 89 | both squares 90 | Therefore there are (5*5)*(5!) equivalent squares = 3000 91 | 92 | """ 93 | -------------------------------------------------------------------------------- /2019/2019 Q3 - Block-Chain.py: -------------------------------------------------------------------------------- 1 | import time 2 | from functools import lru_cache 3 | 4 | alpha = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") 5 | l, p = input().split() 6 | l = int(l) 7 | 8 | start = time.time() 9 | 10 | @lru_cache(maxsize=None) 11 | def chains(length, smaller, middle, larger): 12 | if length >= 3: return 0 13 | if sum([smaller, middle, larger]) == 0: return 1 14 | ans = [0] 15 | if length == 1: 16 | for i in range(smaller): 17 | ans.append(chains(length, i, middle, larger + smaller - i - 1)) 18 | else: 19 | for i in range(smaller): 20 | ans.append(chains(length, i, middle+smaller-i-1, larger)) 21 | for i in range(middle): 22 | ans.append(chains(length, smaller, i, larger+middle-i-1)) 23 | for i in range(larger): 24 | ans.append(chains(length + 1, smaller, middle+i, larger-i-1)) 25 | return sum(ans) 26 | 27 | def generateStart(l, p): 28 | r = list(alpha[:l]) 29 | for item in p: 30 | r.remove(item) 31 | return r 32 | 33 | def getSeconds(word): 34 | s = [] 35 | for a in range(1,len(word)): 36 | if word[a] > min(word[:a]): 37 | s.append(word[a]) 38 | return s 39 | 40 | remaining = generateStart(l,p) 41 | seconds = getSeconds(p) 42 | length = 1 if len(seconds) == 0 else 2 43 | smaller = 0 44 | middle = 0 45 | larger = 0 46 | for item in remaining: 47 | if item < min(p): 48 | smaller += 1 49 | elif seconds and item < min(seconds): 50 | middle += 1 51 | else: 52 | larger += 1 53 | 54 | print(chains(length, smaller, middle, larger)) 55 | 56 | print("Time:", time.time() - start) 57 | 58 | """ 59 | b) BOI, OBI, OIB, IBO, IOB 60 | 61 | c) Both of the original block chains were in reverse alphabetical order. If the combined blocks chains were not a block 62 | chain then at least one of the original block chains must have contained 2 letters in alphabetical order. Thus neither 63 | of the original block chains could have had 2 letters in order as the combined block chain would have had at least 3 64 | letters in alphabetical order. The only way for no 2 letters to be in alphabetical order in a block chain is for the 65 | block chain to be in reverse alphabetical order. This means that the resultant block chain only has a maximum of 2 66 | letters in alphabetical order: one from each of the original block chains. 67 | 68 | d) ASRQPONMLKJIHGFEDCB 69 | RPSMJQHOFEDNLKICGBA 70 | 71 | """ 72 | 73 | def d(r, n, L): 74 | if len(r) == l: return r 75 | if n == 0: return r 76 | for a in alpha: 77 | if a in r: continue 78 | word = r + a 79 | remaining = generateStart(L, word) 80 | seconds = getSeconds(word) 81 | length = 1 if len(seconds) == 0 else 2 82 | smaller = 0 83 | middle = 0 84 | larger = 0 85 | for item in remaining: 86 | if item < min(word): 87 | smaller += 1 88 | elif seconds and item < min(seconds): 89 | middle += 1 90 | else: 91 | larger += 1 92 | m = chains(length, smaller, middle, larger) 93 | if n <= m: 94 | return d(word, n, L) 95 | n -= m 96 | 97 | #print(d("", 1000000000, 19)) 98 | -------------------------------------------------------------------------------- /2023/2023 - Q1 Zeckendorf Representation.py: -------------------------------------------------------------------------------- 1 | # get input 2 | n = int(input()) 3 | 4 | def zeckendorf(n): 5 | 6 | # generate fib sequence up to n 7 | def gen_fib(n): 8 | a, b = 1, 1 9 | fib = [a, b] 10 | while a < n: 11 | # if sum(fib) == n: print("!") 12 | c = a + b 13 | b = a 14 | a = c 15 | fib.append(c) 16 | # print(sum(fib) - n - a) 17 | return fib 18 | 19 | fib_seq = gen_fib(n) 20 | # pointer to current index in fib sequence 21 | ptr = len(fib_seq) - 1 22 | ans = [] 23 | 24 | # generate z sequence 25 | while n > 0: 26 | # can we use this number? 27 | if fib_seq[ptr] <= n: 28 | n -= fib_seq[ptr] 29 | ans.append(str(fib_seq[ptr])) 30 | # skip the next number 31 | ptr -= 1 32 | ptr -= 1 33 | 34 | # print(gen_fib(5000000000)) 35 | return ans 36 | 37 | 38 | ans = zeckendorf(n) 39 | 40 | # output 41 | print(" ".join(ans)) 42 | 43 | """ 44 | 1 (b) This is simply finding the largest number in the fib sequence < 1,000,000 45 | ANS : 832040 46 | 47 | """ 48 | 49 | def n_seq(n): 50 | ans = [] 51 | 52 | for i in range(1, n+1): 53 | z = zeckendorf(i) 54 | ans.append(str(len(z))) 55 | 56 | return ans 57 | 58 | #n_seq(n) 59 | """ 60 | 61 | 1 (c) Consider first 20 numbers in z representation 62 | 1 - 1 {1} 11 - 8 3 {2} 63 | 2 - 2 {1} 12 - 8 3 1 {3} 64 | 3 - 3 {1} 13 - 13 {1} 65 | 4 - 3 1 {2} 14 - 13 1 {2} 66 | 5 - 5 {1} 15 - 13 2 {2} 67 | 6 - 5 1 {2} 16 - 13 3 {2} 68 | 7 - 5 2 {2} 17 - 13 3 1 {3} 69 | 8 - 8 {1} 18 - 13 5 {2} 70 | 9 - 8 1 {2} 19 - 13 5 1 {3} 71 | 10- 8 2 {2} 20 - 13 5 2 {3} 72 | 73 | Consider the n-sequence, the length of the nth z sequence. 74 | The sequence has been split so that each 1 starts a new line: 75 | 1 {1} 76 | 1 {1} 77 | 1 2 {2} 78 | 1 2 2 {3} 79 | 1 2 2 2 3 {5} 80 | 1 2 2 2 3 2 3 3 {8} 81 | 1 2 2 2 3 2 3 3 2 3 3 3 4 {13} 82 | 1 2 2 2 3 2 3 3 [ 2 3 3 ] 3 4 2 3 3 3 4 3 4 4 {21} 83 | 1 2 2 2 3 2 3 3 2 3 3 3 4 2 3 3 3 4 3 4 4 2 3 3 3 4 3 4 4 3 4 4 4 5 {34} 84 | The length of each line is a term in the fibonacci sequence 85 | Now consider how many 3s are in each line: 86 | 0 0 0 0 1 3 6 10 15 87 | These are the triangle numbers. 88 | Thus, we can use this information to determine how many numbers have a z sequence of length 3 89 | 53,316,291,173 is a fib number, making this easier 90 | 91 | ANS : 18424 92 | """ 93 | 94 | # n must be a fibonacci number 95 | def c(n): 96 | N = n 97 | n -= 7 # the first 7 terms contain no 3s 98 | tri = 1 99 | tri_incr = 2 100 | a, b = 3, 5 101 | ans = 0 102 | 103 | # work our way up the sequence 104 | while n >= b: 105 | n -= b 106 | c = a + b 107 | a = b 108 | b = c 109 | ans += tri 110 | tri += tri_incr 111 | tri_incr += 1 112 | 113 | print(ans) 114 | 115 | #c(53316291173) 116 | 117 | """ 118 | The fib number after 701,408,733 is 119 | 120 | consider the numbers with 3 in their z sequence 121 | 3, 4, 11, 12, 16, 17, 24, 25, 32, 33, 37, 38, 45 122 | +1 +7 +1 +4 +1 +7 +1 +7 +1 +4 +1 +7 123 | 124 | """ 125 | -------------------------------------------------------------------------------- /1998/1998 Q3 - Alphametics (Cryptarithms).py: -------------------------------------------------------------------------------- 1 | from itertools import permutations, combinations_with_replacement 2 | import time 3 | 4 | """ 5 | NOTE THAT THIS SOLUTION IS FAR TOO SLOW FOR SOME TEST CASES 6 | """ 7 | 8 | n = int(input()) 9 | 10 | sumWords = [] 11 | totWord = "" 12 | for i in range(n-1): 13 | sumWords.append(input()) 14 | totWord = input() 15 | 16 | start = time.time() 17 | 18 | def output(switch): 19 | global sumWords, totWord 20 | out = "" 21 | for item in sumWords: 22 | out += "".join(switch[l] for l in item) 23 | out += " + " 24 | out = out[:-2] 25 | out += "= " + "".join(switch[l] for l in totWord) 26 | print(out) 27 | 28 | def test(words, tot): 29 | t = 0 30 | for item in words: 31 | t += int(item) 32 | return t == int(tot) 33 | 34 | 35 | def solve(equation): 36 | 37 | # letters begin with startletters 38 | sumWords = equation.replace("=","+").replace(" ", "").split("+") 39 | startLetters = {word[0] for word in sumWords} 40 | letters = sorted(set("".join(sumWords)).difference(startLetters)) 41 | numbers = "1234567890" 42 | letters = sorted(startLetters) + letters 43 | first = len(startLetters) 44 | 45 | options = list(permutations(numbers, len(letters))) 46 | 47 | solutions = 0 48 | for item in options: 49 | 50 | if "0" in item[:first]: 51 | continue 52 | 53 | switch = str.maketrans("".join(letters), "".join(item)) 54 | w = equation.translate(switch) 55 | 56 | tot = 0 57 | for i in sumWords[:-1]: 58 | tot += int(i.translate(switch)) 59 | if tot == int(sumWords[-1].translate(switch)): 60 | print(w) 61 | solutions += 1 62 | 63 | if solutions == 0: 64 | print("Impossible") 65 | if solutions == 1: 66 | print("Unique") 67 | 68 | return solutions 69 | 70 | equation = "" 71 | for item in sumWords: 72 | equation += item + " + " 73 | equation = equation[:-2] 74 | equation += "= " + totWord 75 | 76 | solve(equation) 77 | 78 | print("Time:", time.time() - start) 79 | 80 | """ 81 | b) 309 + 19865 += 20174 82 | 309 + 19847 += 20156 83 | 509 + 39862 += 40371 84 | 509 + 19674 += 20183 85 | 509 + 39817 += 40326 86 | 509 + 19638 += 20147 87 | 609 + 19574 += 20183 88 | 609 + 19538 += 20147 89 | 709 + 59832 += 60541 90 | 709 + 59814 += 60523 91 | 809 + 39562 += 40371 92 | 809 + 59732 += 60541 93 | 809 + 59714 += 60523 94 | 809 + 19365 += 20174 95 | 809 + 19347 += 20156 96 | 809 + 39517 += 40326 97 | 98 | c) Yes. 99 | A+A+A+A+A+A+A+A+A+A+A = AA 100 | Valid for A = 1-9 101 | 102 | d) 163 103 | 1136 104 | 105 | """ 106 | 107 | def d(): 108 | count = 0 109 | count2 = 0 110 | options = combinations_with_replacement("ABCDE", 3) 111 | seen = set() 112 | for item in options: 113 | for i in permutations(list(item)): 114 | i = "".join(i) 115 | if i not in seen: 116 | seen.add(i) 117 | s = solve("ABC + DEA = " + i) 118 | count2 += s 119 | if s: count += 1 120 | options = combinations_with_replacement("ABCDE", 4) 121 | for item in options: 122 | for i in permutations(list(item)): 123 | i = "".join(i) 124 | if i not in seen: 125 | seen.add(i) 126 | s = solve("ABC + DEA = " + i) 127 | count2 += s 128 | if s: count += 1 129 | 130 | print(count, count2) 131 | 132 | #d() -------------------------------------------------------------------------------- /2006/2006 Q2 - Rules.py: -------------------------------------------------------------------------------- 1 | rule = input() 2 | 3 | passwords = [input() for _ in range(2)] 4 | 5 | def check(last, word, rule): 6 | if len(word) == 0 and len(rule) == 0: 7 | return True 8 | if len(word) == 0 or len(rule) == 0: return False 9 | l = word.pop(0) 10 | r = rule[0] 11 | ans = False 12 | if r == "x": 13 | ans = ans or check(l, word, rule[1:]) 14 | elif r == "u": 15 | if int(l) > int(last): 16 | ans = ans or check(l, word, rule[1:]) 17 | else: return False 18 | elif r == "d": 19 | if int(l) < int(last): 20 | ans = ans or check(l, word, rule[1:]) 21 | else: return False 22 | else: 23 | pass 24 | return ans 25 | 26 | allRules = [rule] 27 | if "?" in rule or "*" in rule: 28 | allRules = set() 29 | # replace rules so they don't have special characters 30 | pending = [("", rule)] 31 | while len(pending) > 0: 32 | item, r = pending.pop(0) 33 | if len(r) == 0: 34 | allRules.add(item) 35 | else: 36 | if r[0] in ["x", "d", "u"]: 37 | pending.append((item + r[0], r[1:])) 38 | elif r[0] == "?": 39 | pending.append((item[:-1], r[1:])) 40 | pending.append((item, r[1:])) 41 | elif r[0] == "*": 42 | # up to length 12 43 | w = str(item) 44 | pending.append((item[:-1], r[1:])) 45 | while len(w) < 12: 46 | pending.append((w, r[1:])) 47 | w += w[-1] 48 | elif r[0] == "(": 49 | extra = "" 50 | e = "*" 51 | c = 0 52 | while r[c] != ")": 53 | c += 1 54 | extra += r[c] 55 | c += 2 56 | e = r[c-1] 57 | extra = extra.replace(")", "") 58 | # up to length twelve 59 | if e == "?": 60 | pending.append((item, r[c:])) 61 | pending.append((item + extra, r[c:])) 62 | elif e == "*": 63 | w = str(item) 64 | while len(w) < 12: 65 | pending.append((w, r[c:])) 66 | w += extra 67 | 68 | def bigCheck(word): 69 | ans = False 70 | q = "".join(word) 71 | for item in allRules: 72 | # print(word, item) 73 | ans = ans or check(0, list(q), item) 74 | return ans 75 | 76 | for word in passwords: 77 | a = "Yes" if bigCheck(list(word)) else "No" 78 | print(a) 79 | 80 | #print(allRules) 81 | 82 | """ 83 | b) 46010 84 | Neither Option = 10 85 | Right Option = 100 * 10 = 1000 86 | Left Option = 45 * 10 = 450 87 | Both Options = 45 * 100 * 10 = 45000 88 | All = 45000 + 1000 + 450 + 10 = 46460 89 | Minus 450 because they are the same passwords as included in 1000 90 | 91 | c) x(ud)*u? 92 | All passwords start with x. 93 | (ud)* will check for up down zigzags. 94 | u? allows the password to rise again after the final down 95 | 96 | d) No. For a unique password there must be a string of 9 ups or 9 downs (after the initial x) as only a string of ups 97 | or downs will continue to restrict the password options. However, a string of 10 ups or downs (after the initial x) will 98 | result in 0 valid passwords. Adding a * or ? to a sequence increases the number of valid passwords. Adding an x or 99 | breaking the string of ups or downs increases the number of valid passwords. 100 | """ -------------------------------------------------------------------------------- /2024/2024 - Q3 Word Game.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | def score_word(word): 4 | global ALPHA 5 | s = 0 6 | for i in word: 7 | s += ALPHA.index(i) + 1 8 | return s 9 | 10 | @lru_cache(maxsize=None) 11 | def count_words(score, number): 12 | if score < 0: return 0 13 | if score == 0: return 1 14 | ans = 0 15 | for i in range(min(score, 26)): 16 | j = i+1 17 | if j != number: 18 | ans += count_words(score - j, j) 19 | return ans 20 | 21 | def get_word(n, score): 22 | word = "" 23 | n -= 1 24 | s = 0 25 | last = -1 26 | while s < score: 27 | for i in range(26): 28 | j = i+1 29 | if j == last: continue 30 | x = count_words(score - s - j, j) 31 | if n < x: 32 | s += j 33 | last = j 34 | word += ALPHA[i] 35 | break 36 | n -= x 37 | return word 38 | 39 | def get_pos(word): 40 | global ALPHA 41 | 42 | pos = 1 43 | last = -1 44 | score = score_word(word) 45 | for l in word: 46 | for j in range(ALPHA.index(l)): 47 | i = j+1 48 | if i != last: 49 | # print(i, score-i, count_words(score-i, i)) 50 | pos += count_words(score-i, i) 51 | j = ALPHA.index(l) + 1 52 | score -= j 53 | last = j 54 | # print(pos) 55 | return pos 56 | 57 | 58 | ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 59 | 60 | word = input() 61 | print(get_pos(word)) 62 | print(score_word(word)) 63 | print(get_word(get_pos(word), score_word(word))) 64 | 65 | """ 66 | b) ABABAB 67 | c) 954658 68 | d) 69 | The longest word with a score of 54 is either AB * 18 or BA * 18, which have 36 letters 70 | The word that follows AB * 18 is ABABABABABABABABABABABABABABABABABC, which has 35 letters 71 | The word that follows BA * 18 is BABABABABABABABABABABABABABABABABAC, which has 35 letters 72 | The word that precedes BA * 18 is AZYB, which has 4 letters, giving a difference of 32 73 | To achieve a better difference we either need a longer longest word (which there isn't) or a shorter word to be 74 | adjacent to. 75 | Several 3 letter words exist (e.g. CYZ) brute forcing these gives a difference of no greater than 31 76 | e.g. CZY and DABABABABABABABABABABABABABABABACA 77 | No 2 or 1 letter words exist. 78 | Thus, this difference of 32 is the greatest and only occurs once. 79 | 80 | 81 | """ 82 | """ 83 | # part b 84 | print(get_word(1, score_word("ACE"))) 85 | 86 | # part c 87 | print(count_words(26, -1)) 88 | 89 | # part d brute force part 90 | MAX_LEN = 0 91 | end = count_words(54, -1) 92 | for a in range(26): 93 | for b in range(26): 94 | for c in range(26): 95 | if a == b or b == c or a == c: continue 96 | total = a + b + c + 3 97 | if total != 54: continue 98 | word = ALPHA[a] + ALPHA[b] + ALPHA[c] 99 | i = get_pos(word) 100 | if i > 1: 101 | word_left = get_word(i-1, 54) 102 | if len(word_left) - 3 > MAX_LEN: 103 | MAX_LEN = len(word_left) - 3 104 | print(word, word_left, MAX_LEN) 105 | if i < end: 106 | word_left = get_word(i+1, 54) 107 | if len(word_left) - 3 > MAX_LEN: 108 | MAX_LEN = len(word_left) - 3 109 | print(word, word_left, MAX_LEN) 110 | print("MAX_LEN", MAX_LEN) 111 | """ 112 | -------------------------------------------------------------------------------- /2020/2020 Q2 - Alpha Complex.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from time import time 3 | from copy import deepcopy 4 | 5 | alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 6 | rooms = defaultdict(list) 7 | 8 | plan, p, q = input().split() 9 | plan = list(plan) 10 | p = int(p) 11 | q = int(q) 12 | n = len(plan) + 2 13 | 14 | start = time() 15 | 16 | # generate graph from plan 17 | # list of letters left to choose 18 | to_choose = list(alphabet[:n]) 19 | for i in range(n - 2): 20 | for letter in to_choose: 21 | if letter not in plan: 22 | # connect these rooms 23 | rooms[letter].append(plan[0]) 24 | rooms[plan[0]].append(letter) 25 | # update lists 26 | plan.pop(0) 27 | to_choose.remove(letter) 28 | break 29 | 30 | # connect the two remaining rooms 31 | rooms[to_choose[0]].append(to_choose[1]) 32 | rooms[to_choose[1]].append(to_choose[0]) 33 | 34 | # output graph 35 | for i in range(n): 36 | output = "" 37 | rooms[alphabet[i]] = sorted(rooms[alphabet[i]]) 38 | 39 | for room in rooms[alphabet[i]]: 40 | output += room 41 | 42 | print(output) 43 | 44 | # main code 45 | 46 | position = "A" 47 | # initialise dictionaries 48 | visited_odd = defaultdict(bool) 49 | exited_odd = defaultdict(bool) 50 | 51 | def move(): 52 | global position, visited_odd, exited_odd 53 | 54 | # we have visited this room one more time 55 | visited_odd[position] = not visited_odd[position] 56 | 57 | if visited_odd[position]: 58 | # through first exit alphabetically 59 | # update exit information 60 | next_room = rooms[position][0] 61 | exited_odd[position + next_room] = not exited_odd[position + next_room] 62 | # update position 63 | position = next_room 64 | 65 | else: 66 | # find first exit alphabetically that has been 67 | # left through an odd number of times 68 | for i in range(len(rooms[position])): 69 | if exited_odd[position + rooms[position][i]]: 70 | # is this the last room? 71 | if i < len(rooms[position]) - 1: 72 | i += 1 73 | # update exit information 74 | next_room = rooms[position][i] 75 | exited_odd[position + next_room] = not exited_odd[position + next_room] 76 | # update position 77 | position = next_room 78 | 79 | break 80 | 81 | 82 | 83 | for _ in range(p): 84 | # print(_) 85 | move() 86 | 87 | print(position, end="") 88 | 89 | seen = [] 90 | 91 | for i in range(q - p): 92 | # print(i) 93 | if (position, visited_odd, exited_odd) in seen: 94 | index = seen.index((position, visited_odd, exited_odd)) 95 | index += q - p - i 96 | index %= len(seen) 97 | # print(seen) 98 | position = seen[index][0] 99 | break 100 | else: 101 | seen.append((position, deepcopy(visited_odd), deepcopy(exited_odd))) 102 | move() 103 | 104 | print(position) 105 | 106 | print(time() - start) 107 | 108 | """ 109 | b) A 110 | AAAA 111 | Letters in plan = Number of letters in complex - 2. As all rooms are connected only a A, the plan must only contain 112 | As 113 | 114 | c) If the number of odd exits from the room is even, the room must have been visited an odd number of times. If it is odd, 115 | the room must have been visited an even number of times. For example, the first time a spy is in a room, the number of 116 | odd exits is zero, which is even. 117 | 118 | """ 119 | 120 | -------------------------------------------------------------------------------- /2024/2024 - Q2 Parsing Lists.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | from math import * 3 | from time import time 4 | from itertools import combinations_with_replacement as comb, permutations 5 | 6 | def parse(a, b, i): 7 | return a(b(a(i))) 8 | 9 | def E(i): 10 | return i*2 11 | 12 | def O(i): 13 | return i*2-1 14 | 15 | # Thanks to @prantav for this O(1) function 16 | def T(x): 17 | return ceil(((-1)+((1+(8*x))**0.5))/2) 18 | 19 | def get_bracket(string, i): 20 | depth = 1 21 | i += 1 22 | # print(string,i) 23 | while True: 24 | if string[i] == "(": depth += 1 25 | elif string[i] == ")": 26 | depth -= 1 27 | if depth == 0: return i 28 | i += 1 29 | 30 | def expand_fully(string): 31 | if len(string) == 1: return string 32 | if "(" not in string: 33 | left = string[0] 34 | for i in range(1, len(string)): 35 | right = string[i] 36 | left = right + left + right 37 | 38 | return left 39 | 40 | left = "" 41 | right = "" 42 | right_ptr = 0 43 | 44 | if string[0] == "(": 45 | # cry 46 | bend = get_bracket(string, 0) 47 | left = expand_fully(string[1:bend]) 48 | right_ptr = bend + 1 49 | else: 50 | left = string[0] 51 | right_ptr = 1 52 | 53 | while right_ptr < len(string): 54 | if string[right_ptr] == "(": 55 | # cry 56 | bend = get_bracket(string, right_ptr) 57 | right = expand_fully(string[right_ptr+1:bend]) 58 | left = right + left + right 59 | right_ptr = bend + 1 60 | else: 61 | right = string[right_ptr] 62 | left = right + left + right 63 | right_ptr += 1 64 | 65 | return left 66 | 67 | def parse_fully(string, i): 68 | string = string[::-1] 69 | for c in string: 70 | if c == "E": i = E(i) 71 | if c == "O": i = O(i) 72 | if c == "T": i = T(i) 73 | return i 74 | 75 | string, i = input().split() 76 | i = int(i) 77 | start = time() 78 | string = expand_fully(string) 79 | print(parse_fully(string, i)) 80 | print("Time taken:", time() - start) 81 | 82 | """ 83 | b) 20 84 | c) 41 85 | """ 86 | 87 | def b(): 88 | i = 1 89 | count = 0 90 | x = 0 91 | while x <= 2: 92 | x = parse_fully(expand_fully("TT"), i) 93 | if x == 2: 94 | count += 1 95 | i += 1 96 | print(count) 97 | 98 | #b() 99 | 100 | def has_99(string): 101 | x = 0 102 | i = 1 103 | while x < 99 and i < 100000: 104 | x = parse_fully(string, i) 105 | i += 1 106 | if x == 99: 107 | return True 108 | 109 | 110 | def c(): 111 | letters = "ABCD" 112 | seen = set() 113 | count = 0 114 | options = ["A", "AB", "ABC", "A(BC)", "ABCD", "AB(CD)", "A(BC)D", "A(BCD)", "A(B(CD))"] 115 | for option in options: 116 | i = len(option) - option.count("(") - option.count(")") 117 | for c in comb("OTE", i): 118 | for p in permutations(c, i): 119 | string = option 120 | for j in range(i): 121 | string = string.replace(letters[j], p[j]) 122 | string = expand_fully(string) 123 | if string in seen: 124 | continue 125 | seen.add(string) 126 | print(string, end=" ") 127 | if has_99(string): 128 | print("YES") 129 | count += 1 130 | else: print() 131 | print(count) 132 | #c() 133 | -------------------------------------------------------------------------------- /2009/2009 Q2 - Puzzle Game.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | import time 3 | 4 | grid = [list(input()) for _ in range(4)] 5 | 6 | original = [] 7 | for x in range(4): 8 | a = [] 9 | for y in range(4): 10 | a.append(grid[y][x]) 11 | original.append(deque(a)) 12 | 13 | blocks = list() 14 | seen = set() 15 | 16 | def getBlocks(): 17 | global grid, blocks, seen 18 | seen = set() 19 | blocks = [] 20 | 21 | for y in range(4): 22 | for x in range(4): 23 | if (x,y) not in seen: 24 | curr = [] 25 | pending = [(x,y)] 26 | t = grid[y][x] 27 | while len(pending) > 0: 28 | item = pending.pop(0) 29 | if grid[item[1]][item[0]] == t: 30 | if item not in seen: curr.append(item) 31 | seen.add(item) 32 | if item[1] > 0 and (item[0], item[1] - 1) not in seen: 33 | pending.append((item[0], item[1] - 1)) 34 | # seen.add((item[0], item[1] - 1)) 35 | if item[0] > 0 and (item[0] - 1, item[1]) not in seen: 36 | pending.append((item[0] - 1, item[1])) 37 | # seen.add((item[0] - 1, item[1])) 38 | if item[1] < 3 and (item[0], item[1] + 1) not in seen: 39 | pending.append((item[0], item[1] + 1)) 40 | # seen.add((item[0], item[1] + 1)) 41 | if item[0] < 3 and (item[0] + 1, item[1]) not in seen: 42 | pending.append((item[0] + 1, item[1])) 43 | # seen.add((item[0] + 1, item[1])) 44 | if len(curr) > 1: 45 | blocks.append(curr) 46 | 47 | def removeBlanks(): 48 | global grid 49 | while True: 50 | moved = False 51 | for x in range(4): 52 | for y in range(3): 53 | if grid[y][x] != "-" and grid[y+1][x] == "-": 54 | grid[y+1][x] = grid[y][x] 55 | grid[y][x] = "-" 56 | moved = True 57 | if not moved: break 58 | 59 | # moves things down 60 | while True: 61 | for x in range(4): 62 | if grid[0][x] == "-": 63 | grid[0][x] = original[x][-1] 64 | original[x].rotate(1) 65 | moved = False 66 | for x in range(4): 67 | for y in range(3): 68 | if grid[y][x] != "-" and grid[y+1][x] == "-": 69 | grid[y+1][x] = grid[y][x] 70 | grid[y][x] = "-" 71 | moved = True 72 | if not moved: break 73 | 74 | 75 | def draw(): 76 | for y in range(4): 77 | print("".join(grid[y])) 78 | 79 | 80 | gameOver = False 81 | score = 0 82 | while True: 83 | n = int(input()) 84 | start = time.time() 85 | if n == 0: break 86 | for i in range(n): 87 | getBlocks() 88 | if len(blocks) == 0: 89 | gameOver = True 90 | break 91 | 92 | # Remove blocks 93 | for block in blocks: 94 | for b in block: 95 | grid[b[1]][b[0]] = "-" 96 | 97 | # calculate score 98 | s = 1 99 | for block in blocks: 100 | s *= len(block) 101 | score += s 102 | 103 | removeBlanks() 104 | if gameOver: 105 | print("GAME OVER") 106 | print(score) 107 | break 108 | 109 | draw() 110 | # print(blocks) 111 | print(score) 112 | print("Time:", time.time() - start) 113 | 114 | """ 115 | b) 105 = 5 * 3 * 7 116 | RRRR 117 | BBBR 118 | GGGG 119 | GGGR 120 | 121 | c) 3 * 3 * 3 * 4 = 324 122 | 123 | 124 | """ 125 | -------------------------------------------------------------------------------- /2022/2022 Q3 - Parking.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | from time import * 3 | from math import * 4 | 5 | park, n = input().split() 6 | park = park.lower() 7 | n = int(n) 8 | 9 | start = time() 10 | 11 | alpha = "abcdefghijklmnopqrstuvwxyz" 12 | 13 | @lru_cache(maxsize=None) 14 | def count(filled, j): 15 | if j >= len(filled): return 1 16 | 17 | global park 18 | a = alpha[j] 19 | start = -1 20 | for i in range(len(park)): 21 | if filled[i] == "1": 22 | if start == -1: 23 | start = i 24 | elif park[i] != a: 25 | start = -1 26 | 27 | if park[i] == a: 28 | filled = list(filled) 29 | filled[i] = "1" 30 | filled = "".join(filled) 31 | options = 1 32 | if start > -1: 33 | options = i + 1 - start 34 | return options * count(filled, j+1) 35 | 36 | 37 | @lru_cache(maxsize=None) 38 | def construct(j, filled, n): 39 | if j >= len(filled): return "" 40 | global park 41 | a = alpha[j] 42 | start = -1 43 | 44 | for i in range(len(park)): 45 | if filled[i] == "1": 46 | if start == -1: 47 | start = i 48 | elif park[i] != a: 49 | start = -1 50 | 51 | if park[i] == a: 52 | filled = list(filled) 53 | filled[i] = "1" 54 | filled = "".join(filled) 55 | # this is where we are parked so we 56 | # could prefer any filled point congruent 57 | # to the left 58 | 59 | # if start == -1: 60 | # return alpha[i] + construct(j+1, filled, n) 61 | # there are options 62 | k = start 63 | if k == -1: k = i 64 | while True: 65 | n1 = count(filled, j+1) 66 | if n <= n1: 67 | return alpha[k] + construct(j+1, filled, n) 68 | n -= n1 69 | k += 1 70 | 71 | filled = "0" * len(park) 72 | 73 | #print(count(filled, 0)) 74 | print(construct(0, filled, n).upper()) 75 | #print(time() - start) 76 | 77 | def get_num_prefs(parked, car): 78 | position = parked.index(car) 79 | preferences = 1 80 | 81 | while position > 0: 82 | position -= 1 83 | if parked[position] == ".": 84 | break 85 | preferences += 1 86 | 87 | return preferences 88 | 89 | def count_pref_lists(park): 90 | parked = ["." for _ in range(len(park))] 91 | prefs = 1 92 | for car in range(len(park)): 93 | position = park.index(alpha[car]) 94 | parked[position] = alpha[car] 95 | # get the car's preferred positions 96 | preferred = get_num_prefs(parked, alpha[car]) 97 | # update total number of valid preference lists 98 | prefs *= preferred 99 | return prefs 100 | 101 | from itertools import permutations as perms 102 | 103 | def c(i): 104 | ans = 0 105 | for a in perms(alpha[:i]): 106 | a = "".join(a) 107 | # print(a, count("".join(a))) 108 | if count_pref_lists(a) == 2: 109 | ans += 1 110 | print(ans) 111 | 112 | """ 113 | Triangle Numbers, so 16 is 15th triangle number = 120 114 | 2 1 115 | 3 3 116 | 4 6 117 | 5 10 118 | 6 15 119 | 7 21 120 | 8 28 121 | """ 122 | 123 | def d(): 124 | ans = 0 125 | fact = factorial(15) 126 | for a in range(1,16): 127 | for b in range(1,a): 128 | ans += fact // (a*b) 129 | output = "" 130 | ans = str(ans)[::-1] 131 | for i in range(0, len(ans), 3): 132 | output += ans[i:i+3] + "," 133 | output = output[::-1][1:] 134 | print(output) 135 | #d() 136 | -------------------------------------------------------------------------------- /2023/2023 - Q3 Dreaming Spires.py: -------------------------------------------------------------------------------- 1 | from itertools import permutations 2 | from _collections import deque 3 | 4 | start = "-".join(input().split()).replace("0", "") 5 | end = "-".join(input().split()).replace("0", "") 6 | 7 | q = deque() 8 | q.append((start, 0)) 9 | 10 | seen = set() 11 | 12 | while q: 13 | curr, d = q.popleft() 14 | # print(curr, d) 15 | 16 | if curr == end: 17 | print(d) 18 | break 19 | 20 | curr = curr.split("-") 21 | 22 | for a in range(4): 23 | if curr[a] != "": 24 | for b in range(4): 25 | if a != b: 26 | # move top from a to b 27 | next_state = list(curr) 28 | move = next_state[a][-1] 29 | next_state[a] = next_state[a][:-1] 30 | next_state[b] += move 31 | next_state = "-".join(next_state) 32 | if next_state not in seen: 33 | seen.add(next_state) 34 | q.append((next_state, d+1)) 35 | 36 | """ 37 | b) ball arrangement is 1 1 2 0 balls on each peg in some order to give 9 possible moves. 38 | There are 4!/2! = 12 ways to arrange 1 1 2 0 and 4! = 24 ways of arranging the balls in each arrangement, 39 | giving 288 game states 40 | 41 | check with code: 42 | 43 | states_9 = set() 44 | 45 | for item in permutations(("1", "1", "2", "0")): 46 | for perm in permutations(("1","2","3","4")): 47 | perm = "".join(perm) 48 | state = perm[:int(item[0])] + "-" + perm[int(item[0]):int(item[0]) + int(item[1])] + "-" + perm[int(item[0])+int(item[1]):int(item[0])+int(item[1])+int(item[2])] + "-" + perm[int(item[0])+int(item[1])+int(item[2]):] 49 | print(state) 50 | states_9.add(state) 51 | 52 | print(len(states_9)) 53 | 54 | """ 55 | 56 | """ 57 | c) 73 after 2 moves; 375 after 4 moves 58 | 59 | """ 60 | 61 | def part_c(start, moves): 62 | q = deque() 63 | q.append((start, 0)) 64 | 65 | seen = set() 66 | end = set() 67 | last = -1 68 | 69 | while q: 70 | curr, d = q.popleft() 71 | 72 | if d != last: 73 | last = d 74 | seen.clear() 75 | 76 | if d == moves: 77 | end.add(curr) 78 | continue 79 | 80 | curr = curr.split("-") 81 | 82 | for a in range(4): 83 | if curr[a] != "": 84 | for b in range(4): 85 | if a != b: 86 | # move top from a to b 87 | next_state = list(curr) 88 | move = next_state[a][-1] 89 | next_state[a] = next_state[a][:-1] 90 | next_state[b] += move 91 | next_state = "-".join(next_state) 92 | if next_state not in seen: 93 | seen.add(next_state) 94 | q.append((next_state, d+1)) 95 | 96 | return end 97 | 98 | c = part_c("1-2-3-4", 2) 99 | print(c, len(c)) 100 | 101 | 102 | from itertools import combinations_with_replacement as combi 103 | from functools import lru_cache 104 | 105 | def part_d(): 106 | 107 | @lru_cache(maxsize=None) 108 | def count_states(heights, balls=8, i=0): 109 | # print(heights, balls, i) 110 | if i >= len(heights): 111 | return balls == 0 112 | 113 | ans = 0 114 | for j in range(heights[i]+1): 115 | ans += count_states(heights, balls - j, i+1) 116 | return ans 117 | 118 | print(count_states((3,2,2,2), 4)) 119 | 120 | for heights in combi([1,2,3,4,5,6,7,8], 8): 121 | x = count_states(heights) 122 | if x == 2023: print(heights) 123 | 124 | 125 | part_d() 126 | 127 | -------------------------------------------------------------------------------- /2012/2012 Q3 - Number Ladder.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict 2 | from time import time 3 | 4 | def number_to_string(n): 5 | n = str(n) 6 | string = "" 7 | word = ["ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE"] 8 | for i in n: 9 | string += word[int(i)] 10 | return string 11 | 12 | def count_differences(a,b): 13 | alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 14 | diff = 0 15 | for letter in alpha: 16 | diff += abs(a.count(letter) - b.count(letter)) 17 | return diff 18 | 19 | 20 | def number_ladder(start, end): 21 | 22 | digits = set(str(start)) 23 | for s in str(end): digits.add(s) 24 | digits.add("0") 25 | numbers = [] 26 | for i in range(1, 1000): 27 | j = str(i) 28 | valid = True 29 | for s in j: 30 | if s not in digits: 31 | valid = False 32 | break 33 | if valid: numbers.append(i) 34 | 35 | q_start = deque() 36 | q_start.append(start) 37 | q_end = deque() 38 | q_end.append(end) 39 | 40 | dist_start = defaultdict(int) 41 | dist_start[start] = 1 42 | dist_end = defaultdict(int) 43 | dist_end[end] = 1 44 | 45 | while True: 46 | # start 47 | u = q_start.popleft() 48 | u_string = number_to_string(u) 49 | for i in numbers: 50 | if dist_start[i] == 0 and count_differences(number_to_string(i), u_string) <= 5: 51 | # can move here 52 | q_start.append(i) 53 | dist_start[i] = dist_start[u] + 1 54 | if dist_end[i] != 0: 55 | # meet in the middle 56 | return dist_start[i] + dist_end[i] - 2 57 | # end 58 | u = q_end.popleft() 59 | u_string = number_to_string(u) 60 | for i in numbers: 61 | if dist_end[i] == 0 and count_differences(number_to_string(i), u_string) <= 5: 62 | # can move here 63 | q_end.append(i) 64 | dist_end[i] = dist_end[u] + 1 65 | if dist_start[i] != 0: 66 | # meet in the middle 67 | return dist_start[i] + dist_end[i] - 2 68 | 69 | 70 | total_time = 0 71 | for i in range(3): 72 | start, end = list(map(int, input().split())) 73 | start_t = time() 74 | print(number_ladder(start, end)) 75 | dtime = time() - start_t 76 | total_time += dtime 77 | print(dtime) 78 | print(total_time) 79 | 80 | 81 | # transformations between two numbers with different numbers of digits will also take < 6 steps as we can always add / 82 | # remove a digit we need in one move 83 | def c(): 84 | graph = defaultdict(set) 85 | for i in range(1, 1000): 86 | i_str = number_to_string(i) 87 | for j in range(1, i): 88 | if count_differences(i_str, number_to_string(j)) <= 5: 89 | graph[i].add(j) 90 | graph[j].add(i) 91 | print("graphed") 92 | print(graph) 93 | 94 | sixes = set() 95 | for i in range(100, 1000): 96 | if i % 10 == 0: 97 | print(i) 98 | q = deque() 99 | q.append((i, 0)) 100 | seen = {i} 101 | while q: 102 | u, d = q.popleft() 103 | if d == 6: 104 | sixes.add((i, u)) 105 | continue 106 | for v in graph[u]: 107 | if v not in seen: 108 | seen.add(v) 109 | q.append((v, d+1)) 110 | print(len(sixes)) 111 | 112 | """ 113 | b) 15: [1, 2, 3, 4, 10, 14, 20, 30, 40, 41, 50, 60, 70, 80, 90] 114 | c) 6, 10302 115 | d) Yes. The digit 0 can be transformed to any other digit. Therefore any digit can be transformed to any other digit. 116 | Thus, any integer can be transformed to any other integer. An integer of size n can be transformed to any other integer 117 | of size n by changing each digit in turn. 118 | """ 119 | -------------------------------------------------------------------------------- /2014/2014 Q2 - Loops.py: -------------------------------------------------------------------------------- 1 | 2 | # 0 - up, 1 - right, 2 - down, 3 - left 3 | # 0 red, 1 green 4 | tiles = [[(0,2), (1,3)], [(1,3), (0,2)], [(0,3), (1,2)], [(0,1), (2,3)], [(2,1), (0,3)], [(2,3), (0,1)]] 5 | opposite = {0:2, 2:0, 1:3, 3:1} 6 | n = int(input()) 7 | board = [list(map(int, input().split())) for _ in range(n)] 8 | 9 | #print(board) 10 | 11 | # score loops 12 | def countLoop(player): 13 | seen = set() 14 | score = 0 15 | for Y in range(n): 16 | for X in range(n): 17 | x = X 18 | y = Y 19 | if (x,y) not in seen: 20 | loop = set() 21 | complete = False 22 | t = board[y][x] 23 | move = tiles[t-1][player][1] 24 | while True: 25 | seen.add((x, y)) 26 | if (x,y) in loop: 27 | complete = True 28 | break 29 | loop.add((x,y)) 30 | if move == 0: 31 | # up 32 | y -= 1 33 | if y < 0: break 34 | if opposite[move] in tiles[board[y][x]-1][player]: 35 | a = tiles[board[y][x]-1][player][0] 36 | if a == opposite[move]: 37 | a = tiles[board[y][x]-1][player][1] 38 | move = a 39 | else: 40 | break 41 | elif move == 1: 42 | # right 43 | x += 1 44 | if x >= n: break 45 | if opposite[move] in tiles[board[y][x]-1][player]: 46 | a = tiles[board[y][x]-1][player][0] 47 | if a == opposite[move]: 48 | a = tiles[board[y][x]-1][player][1] 49 | move = a 50 | else: 51 | break 52 | elif move == 2: 53 | # down 54 | y += 1 55 | if y >= n: break 56 | if opposite[move] in tiles[board[y][x]-1][player]: 57 | a = tiles[board[y][x]-1][player][0] 58 | if a == opposite[move]: 59 | a = tiles[board[y][x]-1][player][1] 60 | move = a 61 | else: 62 | break 63 | elif move == 3: 64 | # left 65 | x -= 1 66 | if x < 0: break 67 | if opposite[move] in tiles[board[y][x]-1][player]: 68 | a = tiles[board[y][x]-1][player][0] 69 | if a == opposite[move]: 70 | a = tiles[board[y][x]-1][player][1] 71 | move = a 72 | else: 73 | break 74 | if complete: 75 | score += len(loop) 76 | 77 | return score 78 | 79 | print(countLoop(0), countLoop(1)) 80 | 81 | """ 82 | b) 31 83 | Any of the 6 tiles in the red loop can be switched with a different tile to break the loop (6 * 5) 84 | Also, the one missing tile in the green loop can be placed to increase green's score (1) 85 | 86 | c) 18 87 | 16 can be scored either by a sum of a number of loops: 4, 6, 8, 10, 12, 14, 16. 88 | 16 - 4 + 2 = 6 89 | 12+4 - 4 + 1 = 5 90 | 4+4+4+4 - 1 = 1 91 | 10+6 - This cannot be done 92 | 8+8 - 2 = 2 93 | 4+4+8 - 4 = 4 94 | total = 18 95 | 96 | d) No. Every time a line moves left in a loop it must move right again, similarly for up and down. Therefore, each 97 | loop must contain an even number of tiles. So each player's score must be an even number, and the difference between 98 | them must also always be even. 99 | 100 | """ 101 | 102 | -------------------------------------------------------------------------------- /2022/2022 Q2 - Game of Drones.py: -------------------------------------------------------------------------------- 1 | from time import * 2 | 3 | edges = [[0 for _ in range(25)] for _ in range(25)] 4 | 5 | def control(hex, debug=False): 6 | r = 0 7 | b = 0 8 | for i in range(6): 9 | if edges[hex][i] > 0: 10 | r += 1 11 | if debug: 12 | print("red e", i+1, hex+1) 13 | elif edges[hex][i] < 0: 14 | b += 1 15 | if debug: 16 | print("blue e", i+1, hex+1) 17 | 18 | if r == b: return 0 19 | if r > b: return 1 20 | return -1 21 | 22 | def opposite(edge): 23 | return (edge + 3) % 6 24 | 25 | def own(v, hex, edge): 26 | switched = False 27 | if edges[hex][edge] not in [v, 0]: switched = True 28 | edges[hex][edge] = v 29 | h = hex 30 | hex += 1 31 | e = opposite(edge) 32 | row = h // 5 33 | x, y = 0, 0 34 | if row % 2 == 1: 35 | y = 1 36 | x = 0 37 | else: 38 | y = 0 39 | x = 1 40 | # capture other side of edge 41 | newhex = -1 42 | if edge == 0 and hex > 5 and (hex % 5 > 0 or row % 2 == 0): 43 | newhex = h-4-x 44 | if edge == 1 and hex % 5 > 0: 45 | newhex = h+1 46 | if edge == 2 and hex < 21 and (hex % 5 > 0 or row % 2 == 0): 47 | newhex = h+5+y 48 | if edge == 3 and hex < 21 and (hex % 5 != 1 or row % 2 == 1): 49 | newhex = h+4+y 50 | if edge == 4 and hex % 5 != 1: 51 | newhex = h-1 52 | if edge == 5 and hex > 5 and (hex % 5 != 1 or row % 2 == 1): 53 | newhex = h-5-x 54 | 55 | if newhex != -1: 56 | if edges[newhex][edge] not in [v, 0]: switched = True 57 | edges[newhex][e] = v 58 | 59 | return switched 60 | 61 | r, b = list(map(int, input().split())) 62 | s, f = list(map(int, input().split())) 63 | 64 | start = time() 65 | 66 | # set up game state 67 | def reset(): 68 | global edges, rpos, bpos, redge, bedge 69 | rpos = 0 70 | bpos = 24 71 | redge = 0 72 | bedge = 5 73 | edges = [[0 for _ in range(25)] for _ in range(25)] 74 | 75 | 76 | reset() 77 | 78 | def skirmish(): 79 | global rpos, bpos, redge, bedge, r, b 80 | #red 81 | switch = own(1, rpos, redge) 82 | redge += 1 83 | redge %= 6 84 | rpos += r 85 | rpos %= 25 86 | # blue 87 | s = own(-1, bpos, bedge) 88 | switch = switch or s 89 | bedge -= 1 90 | if bedge < 0: bedge += 6 91 | bpos += b 92 | bpos %= 25 93 | 94 | return switch 95 | 96 | def score(debug = False): 97 | rown, bown = 0, 0 98 | for hex in range(25): 99 | c = control(hex, debug) 100 | if c == 1: 101 | rown += 1 102 | if debug: 103 | print(hex+1, "red") 104 | elif c == -1: 105 | bown += 1 106 | if debug: 107 | print(hex+1, "blue") 108 | return rown, bown 109 | 110 | def feud(): 111 | # red 112 | e = [] 113 | rscore, bscore = score() 114 | for hex in range(25): 115 | for edge in range(6): 116 | if edges[hex][edge] != 0: continue 117 | own(1, hex, edge) 118 | nr, nb = score() 119 | con = nr - rscore 120 | uncon = bscore - nb 121 | # disown edge 122 | own(0, hex, edge) 123 | e.append((con, uncon, -hex, -edge)) 124 | e = sorted(e) 125 | if len(e) > 0: 126 | hex, edge = -e[-1][2], -e[-1][3] 127 | own(1, hex, edge) 128 | 129 | # blue 130 | e = [] 131 | rscore, bscore = score() 132 | for hex in range(25): 133 | for edge in range(6): 134 | if edges[hex][edge] != 0: continue 135 | own(-1, hex, edge) 136 | nr, nb = score() 137 | con = nb - bscore 138 | uncon = rscore - nr 139 | # disown edge 140 | own(0, hex, edge) 141 | e.append((con, uncon, hex, edge)) 142 | e = sorted(e) 143 | if len(e) > 0: 144 | hex, edge = e[-1][2], e[-1][3] 145 | own(-1, hex, edge) 146 | 147 | for i in range(s): 148 | skirmish() 149 | 150 | for i in range(f): 151 | feud() 152 | 153 | rown, bown = score() 154 | 155 | print(rown) 156 | print(bown) 157 | 158 | #print(time() - start) 159 | -------------------------------------------------------------------------------- /1999/1999 Q2 - Black Box (Atom).py: -------------------------------------------------------------------------------- 1 | atoms = set() 2 | entered = set() 3 | 4 | def screenToBoard(x,y): 5 | x -= 1 6 | y = 10 - y 7 | return x,y 8 | 9 | def boardToScreen(x,y): 10 | x += 1 11 | y = 10 - y 12 | return x,y 13 | 14 | def printBoard(): 15 | for y in range(10): 16 | for x in range(10): 17 | if (x,y) in entered: 18 | if (x,y) in atoms: print("*", end="") 19 | else: print("+",end="") 20 | elif(x,y) in atoms: print("A", end="") 21 | else: print(".",end="") 22 | print() 23 | 24 | sideToDir = {"T":"D", "L":"R", "R":"L", "B":"U"} 25 | dirToSide = {"D":"B", "L":"L", "R":"R", "U":"T"} 26 | directions = {"U":(0,-1), "L":(-1,0), "R":(1,0), "D":(0,1)} 27 | 28 | def onBoard(x,y): 29 | return x >= 0 and y >= 0 and y < 10 and x < 10 30 | 31 | def fireRay(side, loc): 32 | global entered, atoms 33 | entered = set() 34 | d = sideToDir[side] 35 | pos = (0,0) 36 | if side == "L": pos = (0, 10-loc) 37 | if side == "R": pos = (9, 10-loc) 38 | if side == "T": pos = (loc-1, 0) 39 | if side == "B": pos = (loc-1, 9) 40 | # print(pos, d) 41 | while True: 42 | entered.add(pos) 43 | if pos in atoms: 44 | return "Absorbed" 45 | if not onBoard(pos[0], pos[1]): 46 | if dirToSide[d] in ["T","B"]: 47 | return "Exits at " + dirToSide[d] + " " + str(boardToScreen(pos[0], pos[1])[0]) 48 | return "Exits at " + dirToSide[d] + " "+ str(boardToScreen(pos[0], pos[1])[1]) 49 | 50 | move = directions[d] 51 | next = (pos[0] + move[0], pos[1] + move[1]) 52 | if next in atoms: 53 | pass 54 | # up right 55 | elif (pos[0]+1, pos[1]-1) in atoms: 56 | d = "D" if d == "R" else "L" 57 | if (pos[0]+1, pos[1]+1) in atoms or (pos[0]-1, pos[1]-1) in atoms: return "Reflected" 58 | # down right 59 | elif (pos[0]+1, pos[1]+1) in atoms: 60 | d = "U" if d == "R" else "L" 61 | if (pos[0]+1, pos[1]-1) in atoms or (pos[0]-1, pos[1]+1) in atoms: return "Reflected" 62 | # up left 63 | elif (pos[0]-1, pos[1]-1) in atoms: 64 | d = "D" if d == "L" else "R" 65 | if (pos[0]-1, pos[1]+1) in atoms or (pos[0]+1, pos[1]-1) in atoms: return "Reflected" 66 | # down left 67 | elif (pos[0]-1, pos[1]+1) in atoms: 68 | d = "U" if d == "L" else "R" 69 | if (pos[0]-1, pos[1]-1) in atoms or (pos[0]+1, pos[1]+1) in atoms: return "Reflected" 70 | move = directions[d] 71 | pos = (pos[0] + move[0], pos[1] + move[1]) 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | for i in range(5): 80 | x,y = list(map(int, input().split())) 81 | x,y = screenToBoard(x,y) 82 | atoms.add((x,y)) 83 | 84 | printBoard() 85 | 86 | while True: 87 | side, loc = input().split() 88 | 89 | if side == "X": break 90 | loc = int(loc) 91 | 92 | result = fireRay(side, loc) 93 | printBoard() 94 | print(result) 95 | 96 | """ 97 | b) 4 98 | .......... 99 | .A........ 100 | .......... 101 | .......... 102 | ....A..... 103 | .......... 104 | .......A.. 105 | .......... 106 | ........A. 107 | .......... 108 | 109 | c) 110 | It can travel straight from top to bottom. 111 | It can be deflected in a kink to the right, up to 4 places, with 112 | It can be deflected in a king to the left, up to 3 places, with 113 | 114 | d) A ray cannot enter a square exactly 3 times. If a ray enters a square twice in the same direction, it must be stuck 115 | in an infinite loop, and so will enter the square infinitely many times. If the ray has entered a square 3 times it 116 | must have either entered that square from the top and bottom, or from the left and right, meaning it must have been 117 | reflected at some point. A reflected ray will always enter squares an even number of times, x times going forwards and 118 | x times going backwards. 119 | 120 | A ray can enter a square exactly 4 times. A ray can enter a square twice due to deflections. If the ray is then reflected, 121 | it passes back through the square twice again. For example: 122 | 123 | ..A...A 124 | ...+++. 125 | A..+.+. 126 | .+++++. 127 | A..+..A 128 | ...+... 129 | 130 | """ -------------------------------------------------------------------------------- /2010/2010 Q2 - Die Tipping.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | class Die(): 4 | def __init__(self): 5 | self.top = 1 6 | self.bottom = 6 7 | self.back = 5 8 | self.front = 2 9 | self.left = 3 10 | self.right = 4 11 | self.heading = "u" 12 | def tipUp(self): 13 | a = self.top 14 | self.top = self.back 15 | self.back = self.bottom 16 | self.bottom = self.front 17 | self.front = a 18 | self.heading = "u" 19 | def tipDown(self): 20 | a = self.top 21 | self.top = self.front 22 | self.front = self.bottom 23 | self.bottom = self.back 24 | self.back = a 25 | self.heading = "d" 26 | def tipLeft(self): 27 | a = self.top 28 | self.top = self.right 29 | self.right = self.bottom 30 | self.bottom = self.left 31 | self.left = a 32 | self.heading = "l" 33 | def tipRight(self): 34 | a = self.top 35 | self.top = self.left 36 | self.left = self.bottom 37 | self.bottom = self.right 38 | self.right = a 39 | self.heading = "r" 40 | 41 | def draw(): 42 | global board, pos 43 | for y in range(pos[1]-1,pos[1]+2): 44 | for x in range(pos[0]-1, pos[0]+2): 45 | if y < 0 or y > 10 or x < 0 or x > 10: 46 | print("x", end="") 47 | else: 48 | print(board[y][x], end="") 49 | print() 50 | 51 | 52 | board = [[1 for _ in range(11)] for _ in range(11)] 53 | 54 | for i in range(3): 55 | l = input().split() 56 | for j in range(len(l)): 57 | board[4+i][4+j] = int(l[j]) 58 | 59 | start = time.time() 60 | 61 | die = Die() 62 | pos = (5,5) 63 | 64 | clock = {"u":"r", "r":"d", "d":"l", "l":"u"} 65 | anticlock = {"u":"l", "l":"d", "d":"r", "r":"u"} 66 | 67 | while True: 68 | n = int(input()) 69 | if n == 0: break 70 | for _ in range(n): 71 | v = board[pos[1]][pos[0]] + die.top 72 | if v > 6: v -= 6 73 | board[pos[1]][pos[0]] = v 74 | curr = board[pos[1]][pos[0]] 75 | if curr in [1,6]: 76 | # move according to heading 77 | move = die.heading 78 | elif curr == 2: 79 | # move 90* clockwise to heading 80 | move = clock[die.heading] 81 | elif curr in [3,4]: 82 | # move 180* to heading 83 | move = clock[clock[die.heading]] 84 | else: 85 | # move 90* anticlock 86 | move = anticlock[die.heading] 87 | 88 | if move == "u": 89 | pos = (pos[0], pos[1] - 1) 90 | die.tipUp() 91 | elif move == "r": 92 | pos = (pos[0] + 1, pos[1]) 93 | die.tipRight() 94 | elif move == "d": 95 | pos = (pos[0], pos[1] + 1) 96 | die.tipDown() 97 | elif move == "l": 98 | pos = (pos[0] - 1, pos[1]) 99 | die.tipLeft() 100 | 101 | if pos[0] < 0: pos = (10, pos[1]) 102 | if pos[0] > 10: pos = (0, pos[1]) 103 | if pos[1] < 0: pos = (pos[0], 10) 104 | if pos[1] > 10: pos = (pos[0], 0) 105 | 106 | draw() 107 | 108 | """ 109 | b) 32 110 | The centre must be a 2 after sum and the other 5 tiles must be 1 or 6 after sum, so 2^5 111 | 112 | c) 8 for 4 tips 113 | The die must move in a square of length 1 tip. There are 4 such squares, each of which can be traversed in 2 directions 114 | 160 for 6 tips 115 | The die can take 2 different shaped paths: 1 square with a forward-back part way through or a 3x2 rectangle. 116 | There are 4 such squares which can be traversed in 2 directions. At each of the 4 corners of the squares, there are 117 | 4 directions for the forward-back move, giving 4*2*4*4 = 128 paths 118 | There are 4 such rectangle paths which can be traversed in 2 directions. Each of these rectangles can also be rotated 119 | in 4 different directions, giving 4*2*4 = 32 path 120 | 121 | d) No. The numbers on the grid and the die's heading and orientation determines which way the die will move, and so 122 | will determine all subsequent moves. There are a finite number of headings and orientations that the die can have 123 | and a finite number of possible grids. Therefore, eventually the die will come back to a position on a grid that it 124 | has been before with the same orientation and heading. Whenever the die returns like this, it must be stuck in a loop 125 | as it will follow the path it took the last time around as everything is identical and so its moves will be identical. 126 | """ -------------------------------------------------------------------------------- /2007/2007 Q2 - Mu Torere.py: -------------------------------------------------------------------------------- 1 | board = list(input()) 2 | centre = board[0] 3 | board = board[1:] 4 | 5 | drawLim = 9999 6 | EMPTY = "E" 7 | 8 | def canMove(piece, board, centre): 9 | posMoves = [] 10 | if centre == piece: 11 | for i in range(len(board)): 12 | if board[i] == EMPTY: 13 | posMoves.append(("c", i)) 14 | for i in range(len(board)): 15 | if board[i] == piece: 16 | if board[i-1] == EMPTY: posMoves.append((i, i-1)) 17 | if board[i-len(board)+1] == EMPTY: posMoves.append((i, i-len(board)+1)) 18 | if centre == EMPTY: 19 | for i in range(len(board)): 20 | if board[i] == piece: 21 | if board[i-1] == piece and board[i-len(board)+1] == piece: 22 | pass 23 | else: 24 | posMoves.append((i, "c")) 25 | 26 | return posMoves 27 | 28 | player1move = True 29 | 30 | 31 | def playMove(): 32 | global player1move, board, centre 33 | piece = "O" if player1move else "X" 34 | otherPiece = "X" if player1move else "O" 35 | moves = canMove(piece, board, centre) 36 | 37 | if len(moves) == 0: 38 | return 2 if player1move else 1 39 | moved = False 40 | 41 | # check if move will make us win 42 | copy = list(board) 43 | c = centre 44 | for m in moves: 45 | copy = list(board) 46 | c = centre 47 | if m[0] == "c": 48 | c = EMPTY 49 | copy[m[1]] = piece 50 | elif m[1] == "c": 51 | c = piece 52 | copy[m[0]] = EMPTY 53 | else: 54 | copy[m[0]] = EMPTY 55 | copy[m[1]] = piece 56 | if len(canMove(otherPiece, copy, c)) == 0: 57 | moved = True 58 | break 59 | if moved: 60 | board = list(copy) 61 | centre = c 62 | return 1 if player1move else 2 63 | 64 | # stop moves which will make us lose 65 | toRemove = [] 66 | copy = list(board) 67 | c = centre 68 | for m in moves: 69 | copy = list(board) 70 | c = centre 71 | if m[0] == "c": 72 | c = EMPTY 73 | copy[m[1]] = piece 74 | elif m[1] == "c": 75 | c = piece 76 | copy[m[0]] = EMPTY 77 | else: 78 | copy[m[0]] = EMPTY 79 | copy[m[1]] = piece 80 | for m2 in canMove(otherPiece, list(copy), c): 81 | copy2 = list(copy.copy()) 82 | c2 = c 83 | if m2[0] == "c": 84 | c2 = EMPTY 85 | copy2[m2[1]] = otherPiece 86 | elif m2[1] == "c": 87 | c2 = piece 88 | copy2[m2[0]] = EMPTY 89 | else: 90 | copy2[m2[0]] = EMPTY 91 | copy2[m2[1]] = otherPiece 92 | if len(canMove(piece, copy2, c2)) == 0: 93 | toRemove.append(m) 94 | for item in toRemove: 95 | moves.remove(item) 96 | 97 | if len(moves) == 0: 98 | moves = canMove(piece, board, centre) 99 | 100 | # do the left hand most move 101 | m = moves[0] 102 | if m[0] == "c": 103 | centre = EMPTY 104 | board[m[1]] = piece 105 | elif m[1] == "c": 106 | centre = piece 107 | board[m[0]] = EMPTY 108 | else: 109 | board[m[0]] = EMPTY 110 | board[m[1]] = piece 111 | 112 | return 0 113 | 114 | def draw(): 115 | print(centre, end="") 116 | print("".join(board)) 117 | 118 | win = 0 119 | counter = 0 120 | while True: 121 | q = input() 122 | if q == "n": 123 | win = playMove() 124 | if win > 0: break 125 | player1move = not player1move 126 | else: 127 | while True: 128 | win = playMove() 129 | if win > 0: break 130 | player1move = not player1move 131 | counter += 1 132 | if counter > drawLim: break 133 | break 134 | draw() 135 | if win > 0: draw() 136 | if win == 1: print("Player 1 wins") 137 | elif win == 2: print("Player 2 wins") 138 | else: print("Draw") 139 | 140 | """ 141 | b) 142 | XOEXXXOOO 143 | XOOXXXOOE 144 | strategy gives XOOXXXOOE, else player 1 would lose 145 | 146 | c) No. If a player has a piece in the centre they will always be able to move. For a position to be losing there must be 147 | a piece in the centre. If neither player has a piece in the centre, then they will both have at least one piece adjacent 148 | to one of the other player's pieces and so will be able to move that piece into the centre. 149 | 150 | d) 46? Why not 48? 151 | Layouts where centre is empty: 8 152 | Layouts where X is in centre: 19? Why not 20? 153 | Layouts where O is in centre: 19? Why not 20? 154 | """ -------------------------------------------------------------------------------- /2013/2013 Q2 - Neutron.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | neutron = (2,2) 4 | player1 = {i:(i, 4) for i in range(5)} 5 | player2 = {i:(i, 0) for i in range(5)} 6 | 7 | order1 = [int(i)-1 for i in input().split()] 8 | order2 = [int(i)-1 for i in input().split()] 9 | 10 | start = time.time() 11 | 12 | border = [] 13 | for i in range(-1, 6): 14 | border.append((i, -1)) 15 | border.append((i, 5)) 16 | border.append((5, i)) 17 | border.append((-1, i)) 18 | 19 | def draw(): 20 | for y in range(5): 21 | for x in range(5): 22 | if (x,y) == neutron: 23 | print("*", end="") 24 | elif (x,y) in player1.values(): 25 | print("F", end="") 26 | elif (x,y) in player2.values(): 27 | print("S", end="") 28 | else: 29 | print(".", end="") 30 | print() 31 | print() 32 | 33 | def canMove(piece, dir): 34 | moves = [(0,-1), (1,-1), (1,0), (1,1), (0,1), (-1,1), (-1,0), (-1,-1)] 35 | dir = moves[dir] 36 | piece = (piece[0] + dir[0], piece[1] + dir[1]) 37 | if piece == neutron or piece in player1.values() or piece in player2.values() or piece in border: 38 | return False 39 | while True: 40 | piece = (piece[0] + dir[0], piece[1] + dir[1]) 41 | if piece == neutron or piece in player1.values() or piece in player2.values() or piece in border: 42 | return (piece[0] - dir[0], piece[1] - dir[1]) 43 | 44 | def canMoveAtAll(piece): 45 | for dir in range(7): 46 | m = canMove(piece, dir) 47 | if m: return m 48 | return False 49 | 50 | def moveNeutron(p1, piece): 51 | # return the player number of the winner 52 | global neutron 53 | if p1: 54 | for move in [3,4,5]: 55 | m = canMove(neutron, move) 56 | if m and m[1] == 4: 57 | neutron = m 58 | return 1 59 | for move in [0,1,2,3,4,5,6,7]: 60 | m = canMove(neutron, move) 61 | if m and m[1] != 0: 62 | n = neutron 63 | neutron = m 64 | # need to check that we will still be able to move our piece 65 | if canMoveAtAll(player1[piece]): 66 | return False 67 | neutron = n 68 | for move in [0,1,7]: 69 | if canMove(neutron, move): 70 | neutron = canMove(neutron, move) 71 | return 2 72 | 73 | else: 74 | # try to win 75 | for move in [0,1,7]: 76 | m = canMove(neutron, move) 77 | if m and m[1] == 0: 78 | neutron = m 79 | return 2 80 | # move if other player doesn't win 81 | for move in [0,1,2,3,4,5,6,7]: 82 | m = canMove(neutron, move) 83 | if m and m[1] != 4: 84 | n = neutron 85 | neutron = m 86 | # need to check that we will still be able to move our piece 87 | if canMoveAtAll(player2[piece]): 88 | return False 89 | neutron = n 90 | # move to make other player win 91 | for move in [3,4,5]: 92 | if canMove(neutron, move): 93 | neutron = canMove(neutron, move) 94 | return 1 95 | 96 | for move in range(7): 97 | if canMove(neutron, move): 98 | neutron = canMove(neutron, move) 99 | return False 100 | return 2 if p1 else 1 101 | 102 | def movePiece(p1, n): 103 | # return true if successful move 104 | if p1: 105 | for move in range(7): 106 | m = canMove(player1[n], move) 107 | if m: 108 | player1[n] = m 109 | return True 110 | else: 111 | for move in range(7): 112 | m = canMove(player2[n], move) 113 | if m: 114 | player2[n] = m 115 | return True 116 | return False 117 | 118 | 119 | turn = 0 120 | print(order1, order1[turn % 5]) 121 | always = False 122 | while True: 123 | # player 1 move 124 | winner = moveNeutron(True, order1[turn % 5]) 125 | if winner: break 126 | movePiece(True, order1[turn % 5]) 127 | 128 | if turn == 0 or always: draw() 129 | 130 | # player 2 move 131 | winner = moveNeutron(False, order2[turn % 5]) 132 | if winner: break 133 | movePiece(False, order2[turn % 5]) 134 | 135 | if turn == 0 or always: draw() 136 | 137 | turn += 1 138 | 139 | draw() 140 | 141 | print("Time:", time.time() - start) 142 | 143 | """ 144 | b) 65 145 | The neutron can be moved in 6 directions. 146 | UP = 1 move as we will win 147 | RIGHT = 15 moves 148 | DOWN = 15 moves 149 | DOWNLEFT = 1 move as we will lose 150 | LEFT = 16 moves 151 | UPLEFT = 17 moves 152 | 1 + 15 + 15 + 1 + 16 + 17 = 65 153 | 154 | c) 155 | """ -------------------------------------------------------------------------------- /2017/2017 Q2 - Dots and Boxes.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | # Modifer, Position 3 | player1 = [] 4 | pos = 1 5 | mod = 0 6 | player2 = [] 7 | 8 | # dicts of positions and num edges and positions and winner 9 | squares = defaultdict(int) 10 | wins = defaultdict(int) 11 | lines = defaultdict(list) 12 | 13 | # Process input 14 | line = list(map(int, input().split())) 15 | player1 = [line[1], line[0]] 16 | player2 = [line[3], line[2]] 17 | t = line[4] 18 | 19 | def canU(p): 20 | return p > 6 21 | def canD(p): 22 | return p < 31 23 | def canL(p): 24 | return not (p % 6 == 1) 25 | def canR(p): 26 | return not (p % 6 == 0) 27 | 28 | # each line it is a turn 29 | player1turn = True 30 | count = 0 31 | while count < t: 32 | 33 | # update info 34 | if player1turn: 35 | player1[pos] += player1[mod] 36 | if player1[pos] > 36: player1[pos] -= 36 37 | p = player1[pos] 38 | else: 39 | player2[pos] += player2[mod] 40 | if player2[pos] > 36: player2[pos] -= 36 41 | p = player2[pos] 42 | 43 | # Process Move 44 | # print(player1turn) 45 | while True: 46 | if len(lines[p]) < 4: 47 | if player1turn: 48 | # clockwise 49 | if canU(p) and "u" not in lines[p]: 50 | # Put line up 51 | lines[p].append("u") 52 | lines[p-6].append("d") 53 | # print(p, p-6) 54 | break 55 | elif canR(p) and "r" not in lines[p]: 56 | # Put line right 57 | lines[p].append("r") 58 | lines[p + 1].append("l") 59 | # print(p, p + 1) 60 | break 61 | elif canD(p) and "d" not in lines[p]: 62 | # Put line down 63 | lines[p].append("d") 64 | lines[p+6].append("u") 65 | # print(p, p+6) 66 | break 67 | elif canL(p) and "l" not in lines[p]: 68 | # Put line left 69 | lines[p].append("l") 70 | lines[p - 1].append("r") 71 | # print(p, p - 1) 72 | break 73 | else: 74 | # anti-clockwise 75 | if canU(p) and "u" not in lines[p]: 76 | # Put line up 77 | lines[p].append("u") 78 | lines[p-6].append("d") 79 | # print(p, p-6) 80 | break 81 | elif canL(p) and "l" not in lines[p]: 82 | # Put line left 83 | lines[p].append("l") 84 | lines[p - 1].append("r") 85 | # print(p, p - 1) 86 | break 87 | elif canD(p) and "d" not in lines[p]: 88 | # Put line down 89 | lines[p].append("d") 90 | lines[p+6].append("u") 91 | # print(p, p+6) 92 | break 93 | elif canR(p) and "r" not in lines[p]: 94 | # Put line right 95 | lines[p].append("r") 96 | lines[p + 1].append("l") 97 | # print(p, p + 1) 98 | break 99 | p += 1 100 | if p > 36: p -= 36 101 | 102 | if player1turn: player1[pos] = p 103 | else: player2[pos] = p 104 | 105 | # Check if any boxes have been captured 106 | hasCaptured = False 107 | for y in range(5): 108 | for x in range(5): 109 | if wins[(x,y)] == 0: 110 | # box is currently uncaptured 111 | topleft = y*6 + x + 1 112 | topright = y*6 + x + 2 113 | bottomleft = (y+1)*6 + x + 1 114 | bottomright = (y+1)*6 + x + 2 115 | if "d" in lines[topleft] and "l" in lines[topright] and "u" in lines[bottomright] and "r" in lines[bottomleft]: 116 | # captured! 117 | wins[(x,y)] = 1 if player1turn else 2 118 | # print("player" + str(wins[(x,y)]) + "captured box " + str(x) + ", " + str(y)) 119 | hasCaptured = True 120 | 121 | if not hasCaptured: 122 | player1turn = not player1turn 123 | count += 1 124 | 125 | count1 =0 126 | count2=0 127 | for y in range(5): 128 | for x in range(5): 129 | if wins[(x,y)] == 0: 130 | print("*", end="") 131 | elif wins[(x,y)] == 1: 132 | print("X", end="") 133 | count1 += 1 134 | else: 135 | print("O", end="") 136 | count2 += 1 137 | print() 138 | 139 | print(count1, count2) 140 | 141 | 142 | -------------------------------------------------------------------------------- /2011/2011 Q2 - Acordion Patience.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict 2 | from functools import lru_cache 3 | import time 4 | 5 | VALUES = list("A23456789TJQK") 6 | CLUBS = [i + "C" for i in VALUES] 7 | HEARTS = [i + "H" for i in VALUES] 8 | SPADES = [i + "S" for i in VALUES] 9 | DIAMONDS = [i + "D" for i in VALUES] 10 | CARDS = CLUBS + HEARTS + SPADES + DIAMONDS 11 | 12 | order = list(map(int, input().split())) 13 | startTime = time.time() 14 | 15 | piles = [] 16 | 17 | def printPiles(): 18 | global piles 19 | for pile in piles: 20 | print(pile[0], end=" ") 21 | print() 22 | 23 | def size(arr): 24 | size = 0 25 | for item in arr: 26 | size += len(item) 27 | return size 28 | 29 | def reset(): 30 | global piles, CARDS 31 | cards = deque(CARDS) 32 | piles = [] 33 | c = 0 34 | while cards: 35 | cards.rotate(-(order[c%len(order)]-1)) 36 | piles.append(cards.popleft()) 37 | c += 1 38 | 39 | @lru_cache(maxsize=None) 40 | def validMoves(piles): 41 | moves = defaultdict(list) 42 | for i in range(len(piles)): 43 | for j in range(len(piles)): 44 | if i == j-1 or i == j-3: 45 | if piles[i][0] == piles[j][0] or piles[i][1] == piles[j][1]: 46 | moves[j].append(i) 47 | return moves 48 | 49 | reset() 50 | #print(piles) 51 | print(piles[0], piles[-1]) 52 | 53 | # strategy 1 54 | reset() 55 | while len(piles) > 1: 56 | moves = validMoves(tuple(piles)) 57 | if len(moves) == 0: break 58 | p = max(moves.keys()) 59 | q = piles[max(moves[p])] 60 | p = piles.pop(p) 61 | q = piles.index(q) 62 | piles[q] = p 63 | print(len(piles), piles[0]) 64 | 65 | # strategy 2 66 | reset() 67 | while len(piles) > 1: 68 | moves = validMoves(tuple(piles)) 69 | if len(moves) == 0: break 70 | largest = 0 71 | l = 0 72 | for start in sorted(moves.keys()): 73 | for end in sorted(moves[start]): 74 | if len(piles[start]) + len(piles[end]) >= largest: 75 | largest = len(piles[start]) + len(piles[end]) 76 | l = (start, end) 77 | q = piles[l[1]] 78 | p = piles.pop(l[0]) 79 | q = piles.index(q) 80 | piles[q] = p 81 | print(len(piles), piles[0]) 82 | 83 | # strategy 3 84 | reset() 85 | while len(piles) > 1: 86 | moves = validMoves(tuple(piles)) 87 | if len(moves) == 0: break 88 | largest = 0 89 | l = 0 90 | for start in sorted(moves.keys()): 91 | for end in sorted(moves[start]): 92 | # check how many moves this move will leave 93 | pil = list(piles) 94 | q = pil[end] 95 | p = pil.pop(start) 96 | q = pil.index(q) 97 | pil[q] = p 98 | m = size(validMoves(tuple(pil)).values()) 99 | # print(m, largest) 100 | if m >= largest: 101 | largest = m 102 | l = (start, end) 103 | q = piles[l[1]] 104 | p = piles.pop(l[0]) 105 | q = piles.index(q) 106 | piles[q] = p 107 | # printPiles() 108 | print(len(piles), piles[0]) 109 | 110 | print("Time:", time.time() - startTime) 111 | 112 | 113 | 114 | """ 115 | b) 2C, KC, 3H, KH, 4S, KS, 2D, KD, 4C, 2H, 7H, 5S 116 | 117 | c) 531434 118 | 998284 119 | 120 | d) Yes. The first pile that was moved in the first game, only contained 1 card. If a pile with only 1 card is moved onto 121 | another pile then that card and the card beneath it will be adjacent in the second game. If the tiles piles were combined 122 | then the 1 card was a math for the top card of the pile it was moved onto so the 2 adjacent cards will also match and 123 | so they can be combined in the second game. 124 | 125 | """ 126 | 127 | 128 | def c(): 129 | global CARDS 130 | n = 10 131 | seen = set() 132 | for a in range(0,n): 133 | for b in range(0,n): 134 | for c in range(0,n): 135 | for d in range(0,n): 136 | for e in range(0,n): 137 | for f in range(0,n): 138 | cards = deque(CARDS) 139 | ca = set() 140 | cards.rotate(-a) 141 | ca.add(cards.popleft()) 142 | cards.rotate(-b) 143 | ca.add(cards.popleft()) 144 | cards.rotate(-c) 145 | ca.add(cards.popleft()) 146 | cards.rotate(-d) 147 | ca.add(cards.popleft()) 148 | cards.rotate(-e) 149 | ca.add(cards.popleft()) 150 | cards.rotate(-f) 151 | ca.add(cards.popleft()) 152 | ca = tuple(sorted(ca)) 153 | if ca not in seen: 154 | seen.add(ca) 155 | print(len(seen)) 156 | #c() 157 | -------------------------------------------------------------------------------- /2004/2004 Q2 - Four in a Line.py: -------------------------------------------------------------------------------- 1 | board =[["-" for _ in range(7)] for _ in range(6)] 2 | 3 | def willMoveWin(col, p): 4 | row = len(board) - 1 5 | for i in range(len(board)): 6 | if board[i][col] != "-": 7 | row = i - 1 8 | break 9 | if row == -1: return False 10 | 11 | playMove(col, p) 12 | 13 | # draw() 14 | 15 | # check left/right in row 16 | if p * 4 in "".join(board[row]): 17 | removeMove(col) 18 | return True 19 | 20 | # check up/down 21 | h = "" 22 | for i in range(len(board)): h += board[i][col] 23 | if p * 4 in h: 24 | removeMove(col) 25 | return True 26 | 27 | # check diagonals including this point 28 | # check \ 29 | x = max(0, col - row) 30 | y = max(0, row - col) 31 | h = "" 32 | while True: 33 | if x > 6 or y > 5: 34 | break 35 | h += board[y][x] 36 | x += 1 37 | y += 1 38 | if p * 4 in h: 39 | removeMove(col) 40 | return True 41 | 42 | # Check / diagonal 43 | h = "" 44 | # go left first 45 | x = col 46 | y = row 47 | while True: 48 | if x < 0 or y >= len(board): 49 | break 50 | h = board[y][x] + h 51 | x -= 1 52 | y += 1 53 | # go right 54 | x = col + 1 55 | y = row - 1 56 | while True: 57 | if x >= len(board[0]) or y < 0: 58 | break 59 | h += board[y][x] 60 | x += 1 61 | y -= 1 62 | if p * 4 in h: 63 | removeMove(col) 64 | return True 65 | 66 | # otherwise 67 | removeMove(col) 68 | return False 69 | 70 | def removeMove(col): 71 | for i in range(len(board)): 72 | if board[i][col] != "-": 73 | board[i][col] = "-" 74 | return 75 | 76 | def canPlayMove(col): 77 | return board[0][col] == "-" 78 | 79 | def playMove(col, p): 80 | row = -1 81 | for i in range(len(board)): 82 | if board[i][col] != "-": 83 | row = i - 1 84 | break 85 | board[row][col] = p 86 | 87 | def draw(): 88 | for y in range(len(board)): 89 | print("".join(board[y])) 90 | 91 | n = int(input()) 92 | m = list(map(int, input().split())) 93 | 94 | p1 = "*" 95 | p2 = "o" 96 | player1 = True 97 | 98 | for item in m: 99 | p = p1 if player1 else p2 100 | playMove(item-1, p) 101 | player1 = not player1 102 | 103 | noWinner = False 104 | def playOneMove(): 105 | global player1, board, noWinner 106 | p = p1 if player1 else p2 107 | other = p2 if player1 else p1 108 | 109 | # check if can win 110 | for col in range(len(board[0])): 111 | if willMoveWin(col, p): 112 | playMove(col, p) 113 | return True # we have won! 114 | 115 | # check if other player will win 116 | for col in range(len(board[0])): 117 | if willMoveWin(col, other): 118 | playMove(col, p) 119 | return False 120 | 121 | # check if can play anywhere 122 | for i in range(len(board[0])): 123 | if canPlayMove(i): 124 | playMove(i, p) 125 | return False 126 | 127 | # No more spaces to play 128 | noWinner = True 129 | return True 130 | 131 | draw() 132 | 133 | result = False 134 | while not result: 135 | q = input() 136 | if q == "n": 137 | # play 1 move 138 | result = playOneMove() 139 | if result: break 140 | player1 = not player1 141 | else: 142 | # play until finishes 143 | while True: 144 | result = playOneMove() 145 | if result: break 146 | player1 = not player1 147 | break 148 | draw() 149 | 150 | draw() 151 | if noWinner: print("Draw") 152 | elif player1: print("Player 1 wins") 153 | else: print("Player 2 wins") 154 | 155 | """ 156 | b) Player 2 wins 157 | oo**oo- 158 | **oo**- 159 | oo**oo- 160 | **oo**- 161 | oo*oooo 162 | ***o*** 163 | 164 | c) 15 moves 165 | Players take it in turns to play pieces, player 1 plays the odd numbered turns, player 2 plays the even numbered turns. 166 | When player one plays, they have more pieces in play than player 2. The winning move was first played by player 1, 167 | where they have at least 8 pieces in play, thus player 2 would have had 7 pieces in play at this point. 168 | In total, 15 pieces were played to get to this winning moving. The moves to achieve this are below 169 | 1 2 2 3 4 3 4 1 3 2 1 1 2 5 1 170 | 171 | 41 moves is maximum 172 | 173 | d) 60691 174 | """ 175 | """ 176 | code for d ========================================================================================================== 177 | 178 | lists = set() 179 | seen = set() 180 | 181 | def check(item): 182 | if (sum(list(item)) == 21): 183 | print(sum(list(item)), len(lists)) 184 | lists.add(item) 185 | return 186 | for i in range(7): 187 | if item[i] < 6: 188 | q = list(item) 189 | q[i] += 1 190 | if tuple(q) not in seen: 191 | seen.add(tuple(q)) 192 | check(tuple(q)) 193 | 194 | check((0,0,0,0,0,0,0)) 195 | print(len(lists)) 196 | """ -------------------------------------------------------------------------------- /1996/1996 Q3 - Dominoes.py: -------------------------------------------------------------------------------- 1 | from _collections import defaultdict 2 | 3 | connectors = defaultdict(tuple) 4 | values = defaultdict(int) 5 | 6 | dominoes = set() 7 | doubles = [(i, i) for i in range(7)] 8 | 9 | validPlaces = set() 10 | 11 | def generateDominoes(): 12 | global dominoes 13 | dominoes = set() 14 | for a in range(7): 15 | for b in range(7): 16 | if a < b: 17 | dominoes.add((a,b)) 18 | 19 | for y in range(7): 20 | for x in range(8): 21 | validPlaces.add((x,y)) 22 | 23 | for item in validPlaces: 24 | values[item] = -1 25 | 26 | def output(): 27 | for y in range(7): 28 | linebelow = "" 29 | for x in range(8): 30 | space = " " 31 | spaceb = " " 32 | if connectors[(x,y)] == (x+1, y): 33 | space = "-" 34 | elif connectors[(x,y)] == (x, y+1): 35 | spaceb = "| " 36 | linebelow += spaceb 37 | if values[(x,y)] > -1: 38 | print(str(values[(x,y)]) + space, end="") 39 | else: 40 | print("."+ space, end="") 41 | print() 42 | print(linebelow) 43 | 44 | generateDominoes() 45 | 46 | # place fixed dominoes 47 | ans = "" 48 | while True: 49 | line = input() 50 | if line == "-1": break 51 | x,y,d = line.split() 52 | x, y = int(x) - 1, int(y) - 1 53 | second = (0,0) 54 | if d == "B": 55 | second = (x, y+1) 56 | elif d == "R": 57 | second = (x+1, y) 58 | else: 59 | ans = "Impossible" 60 | if (x,y) in validPlaces and second in validPlaces: 61 | validPlaces.remove((x,y)) 62 | validPlaces.remove(second) 63 | tile = doubles.pop(0) 64 | values[(x,y)] = tile[0] 65 | values[second] = tile[0] 66 | connectors[(x,y)] = second 67 | connectors[second] = (x,y) 68 | else: 69 | ans = "Impossible" 70 | 71 | def getAround(pos): 72 | around = set() 73 | directions = [(-1,0), (1,0), (0,1), (0,-1)] 74 | for d in directions: 75 | p = (pos[0] + d[0], pos[1] + d[1]) 76 | if p in validPlaces: 77 | around.add(values[p]) 78 | return around 79 | 80 | def tryPlace(left, pos): 81 | global validPlaces, connectors, values, dominoes 82 | if left: 83 | second = (pos[0] + 1, pos[1]) 84 | left = getAround(pos) 85 | right = getAround(second) 86 | dir = None 87 | for d in dominoes: 88 | if d[0] not in left and d[1] not in right: 89 | dir = d 90 | break 91 | d = d[::-1] 92 | if d[0] not in left and d[1] not in right: 93 | dir = d 94 | break 95 | if dir != None: 96 | values[pos] = dir[0] 97 | values[second] = dir[1] 98 | validPlaces.remove(pos) 99 | validPlaces.remove(second) 100 | connectors[pos] = second 101 | connectors[second] = pos 102 | if dir in dominoes: 103 | dominoes.remove(dir) 104 | else: 105 | dominoes.remove(dir[::-1]) 106 | return True 107 | 108 | return False 109 | else: 110 | second = (pos[0], pos[1] + 1) 111 | left = getAround(pos) 112 | right = getAround(second) 113 | dir = None 114 | for d in dominoes: 115 | if d[0] not in left and d[1] not in right: 116 | dir = d 117 | break 118 | d = d[::-1] 119 | if d[0] not in left and d[1] not in right: 120 | dir = d 121 | break 122 | if dir != None: 123 | values[pos] = dir[0] 124 | values[second] = dir[1] 125 | validPlaces.remove(pos) 126 | validPlaces.remove(second) 127 | connectors[pos] = second 128 | connectors[second] = pos 129 | dominoes.remove(dir) 130 | return True 131 | 132 | return False 133 | 134 | # place all other dominoes 135 | dominoes = dominoes.union(set(doubles)) 136 | for y in range(7): 137 | for x in range(8): 138 | if (x,y) in validPlaces: 139 | # try placing left-right first 140 | if (x+1, y) in validPlaces: 141 | result = tryPlace(True, (x,y)) 142 | if not result: 143 | if (x, y+1) in validPlaces: 144 | result = tryPlace(False, (x,y)) 145 | pass 146 | if not result: 147 | ans = "Impossible" 148 | # try placing up-down else 149 | elif (x, y+1) in validPlaces: 150 | result = tryPlace(False, (x,y)) 151 | if not result: 152 | ans = "Impossible" 153 | else: 154 | ans = "Impossible" 155 | 156 | if ans == "Impossible": 157 | print(ans) 158 | else: 159 | output() 160 | # print(dominoes) 161 | 162 | """ 163 | b) Yes. When can iterate through the permutations of the fixed doubles: 0-6, each of which will give another 164 | valid solution. 165 | 166 | c) 167 | 168 | """ 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /2013/2013 Q3 - Unlock.py: -------------------------------------------------------------------------------- 1 | import time 2 | from collections import deque, defaultdict 3 | 4 | def add(a,b): 5 | return tuple([(a[i] + b[i]) % 3 for i in range(len(a))]) 6 | 7 | 8 | basicV = "010001110001" 9 | leftV = "10000110001" 10 | rightV = "000010001100001" 11 | 12 | vector = defaultdict(tuple) 13 | def createVectors(): 14 | letters = list("BCDGHILMNQRSVWX") 15 | lletters = list("AFKPU") 16 | rletters = list("EJOTY") 17 | for y in range(5): 18 | for x in range(3): 19 | before = y * 5 + x 20 | after = 25 * "0" 21 | v = before * "0" + basicV + after 22 | v = v[5:30] 23 | vector[letters[y*3 + x]] = tuple(map(int, list(v))) 24 | for y in range(5): 25 | before = y * 5 26 | v = before * "0" + leftV + 25 * "0" 27 | v = v[5:30] 28 | vector[lletters[y]] = tuple(map(int, list(v))) 29 | for y in range(5): 30 | before = y * 5 31 | v = before * "0" + rightV + 25 * "0" 32 | v = v[5:30] 33 | vector[rletters[y]] = tuple(map(int, list(v))) 34 | 35 | alpha = list("ABCDEFGHIJKLMNOPQRSTUVWXY") 36 | 37 | def boardToVector(word): 38 | board = [0 for i in range(25)] 39 | for l in word: 40 | if l in alpha: 41 | board[alpha.index(l)] = 2 42 | else: 43 | board[alpha.index(l.upper())] = 1 44 | return tuple(board) 45 | 46 | def solve(startV, allPos = False): 47 | createVectors() 48 | endV = tuple([0 for i in range(25)]) 49 | solved = False 50 | allConfig = [] 51 | for a in range(3): 52 | for b in range(3): 53 | for c in range(3): 54 | for d in range(3): 55 | for e in range(3): 56 | # brute force the top row 57 | # print(a,b,c,d,e) 58 | board = tuple(startV) 59 | for _ in range(a): board = add(vector["A"], board) 60 | for _ in range(b): board = add(vector["B"], board) 61 | for _ in range(c): board = add(vector["C"], board) 62 | for _ in range(d): board = add(vector["D"], board) 63 | for _ in range(e): board = add(vector["E"], board) 64 | solution = "A" * a + "B" * b + "C" * c + "D" * d + "E" * e 65 | # deduce the required moves for the rest of the board 66 | for i in range(20): 67 | # print(board) 68 | if board[i] == 2: 69 | # print(alpha[i+5]) 70 | board = add(vector[alpha[i+5]], board) 71 | solution += alpha[i+5] 72 | elif board[i] == 1: 73 | board = add(vector[alpha[i+5]], add(vector[alpha[i+5]], board)) 74 | solution += alpha[i+5] * 2 75 | if board == endV: 76 | solved = True 77 | if allPos: 78 | display(solution) 79 | allConfig.append(solution) 80 | else: 81 | return solution 82 | if not solved: 83 | print("IMPOSSIBLE") 84 | 85 | return allConfig 86 | 87 | 88 | def display(word): 89 | for letter in alpha: 90 | if word.count(letter) == 1: 91 | print(letter.lower(), end="") 92 | elif word.count(letter) == 2: 93 | print(letter, end="") 94 | print() 95 | 96 | # PRINT ALL 97 | #solve(input(), True) 98 | # PRINT ONE 99 | display(solve(boardToVector(input()))) 100 | 101 | 102 | 103 | """ 104 | For each combination of buttons on the top row, there is only 1 button on the row below that effects that button, so we 105 | now know how many times we can press that button. We can use this to work our way along the rows, row by row. If we 106 | finish and the grid is not zero we know this solution does not work. This means we only have to brute force the top row: 107 | 3^5 = 243 combinations 108 | """ 109 | 110 | """ 111 | b) 11110 = abcdghiJNot 112 | 01112 113 | 00021 114 | 00001 115 | 00000 116 | 117 | c) 598 118 | Lights will only become bright if 2 presses over lap 119 | 120 | 121 | d) No. Every time a button is pressed, the lights change in a manner that is not effected by their current state. If 122 | two systems have different configurations then at least 1 light must be in a different state. By pressing buttons in a 123 | specific sequence, the lights will change in a consistent manner. The sequence of buttons to unlock the first configuration 124 | leaves all lights off. However, since at least one light is in a different state in the second configuration, the same 125 | sequence of button presses will leave the lights in different states in states that are not off. 126 | """ 127 | 128 | def c(): 129 | start = [0 for i in range(25)] 130 | ans = 0 131 | for a in alpha: 132 | for b in alpha: 133 | for c in alpha: 134 | if a < b and b < c: 135 | output = add(vector[c], add(vector[b], add(start, vector[a]))) 136 | if 2 not in output: ans += 1 137 | print(ans) 138 | c() -------------------------------------------------------------------------------- /2016/2016 Q3 - Prime Connections.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | primes = set() 4 | notPrimes = set() 5 | 6 | def isPrime(n): 7 | for i in range(2, int(n**0.5) +1): 8 | if n % i == 0: return False 9 | return True 10 | 11 | ppaths = set() 12 | pdist = dict() 13 | qdist = dict() 14 | qpaths = set() 15 | 16 | l, p, q = list(map(int, input().split())) 17 | 18 | start = time.time() 19 | 20 | ppending = [(p, 1)] 21 | qpending = [(q, 1)] 22 | pdist[p] = 1 23 | qdist[q] = 1 24 | 25 | 26 | # start from p and q 27 | while True: 28 | # start with p 29 | item = ppending.pop(0) 30 | # print(item) 31 | exp = 0 32 | while True: 33 | c = 0 34 | v = item[0] - 2**exp 35 | if v > 1: 36 | if v in primes and v not in pdist: 37 | ppending.append((v, item[1] + 1)) 38 | ppaths.add(v) 39 | pdist[v] = item[1] + 1 40 | elif v in notPrimes: 41 | pass 42 | else: 43 | p = isPrime(v) 44 | if p and v not in pdist: 45 | primes.add(v) 46 | ppending.append((v, item[1] + 1)) 47 | ppaths.add(v) 48 | pdist[v] = item[1] + 1 49 | else: notPrimes.add(v) 50 | else: 51 | c += 1 52 | v = item[0] + 2**exp 53 | if v <= l: 54 | if v in primes and v not in pdist: 55 | ppending.append((v, item[1] + 1)) 56 | ppaths.add(v) 57 | pdist[v] = item[1] + 1 58 | elif v in notPrimes: 59 | pass 60 | else: 61 | p = isPrime(v) 62 | if p and v not in pdist: 63 | primes.add(v) 64 | ppending.append((v, item[1] + 1)) 65 | ppaths.add(v) 66 | pdist[v] = item[1] + 1 67 | else: notPrimes.add(v) 68 | else: 69 | c += 1 70 | 71 | if c == 2: break 72 | 73 | exp += 1 74 | 75 | # now q 76 | # start with p 77 | item = qpending.pop(0) 78 | exp = 0 79 | while True: 80 | c = 0 81 | v = item[0] - 2 ** exp 82 | if v > 1: 83 | if v in primes and v not in qdist: 84 | qpending.append((v, item[1] + 1)) 85 | qpaths.add(v) 86 | qdist[v] = item[1] + 1 87 | elif v in notPrimes: 88 | pass 89 | else: 90 | p = isPrime(v) 91 | if p and v not in qdist: 92 | primes.add(v) 93 | qpending.append((v, item[1] + 1)) 94 | qpaths.add(v) 95 | qdist[v] = item[1] + 1 96 | else: 97 | notPrimes.add(v) 98 | else: 99 | c += 1 100 | v = item[0] + 2 ** exp 101 | if v <= l: 102 | if v in primes and v not in qdist: 103 | qpending.append((v, item[1] + 1)) 104 | qpaths.add(v) 105 | qdist[v] = item[1] + 1 106 | elif v in notPrimes: 107 | pass 108 | else: 109 | p = isPrime(v) 110 | if p and v not in qdist: 111 | primes.add(v) 112 | qpending.append((v, item[1] + 1)) 113 | qpaths.add(v) 114 | qdist[v] = item[1] + 1 115 | else: 116 | notPrimes.add(v) 117 | else: 118 | c += 1 119 | 120 | if c == 2: break 121 | exp += 1 122 | 123 | # check 124 | if len(ppaths.intersection(qpaths)) > 0: break 125 | 126 | 127 | #print(pdist, qdist) 128 | 129 | if q in pdist: 130 | print(pdist[q]) 131 | elif p in qdist: 132 | print(qdist[p]) 133 | else: 134 | shortest = float("inf") 135 | for item in ppaths.intersection(qpaths): 136 | d = pdist[item] + qdist[item] - 1 137 | # print(item, d) 138 | if d < shortest: shortest = d 139 | print(shortest) 140 | print("Time:", time.time() - start) 141 | 142 | """ 143 | b) 12 144 | c) 41041 145 | d) As there are 2 different paths with the same number of primes in between p and q, the shortest 146 | path between p and q must be less than n 147 | """ 148 | 149 | """ 150 | Code for part c: 151 | 152 | paths = set() 153 | 154 | pending = [[2]] 155 | 156 | primes = [2] 157 | 158 | for i in range(3, 250000): 159 | prime = True 160 | for p in primes: 161 | if p > i**0.5: break 162 | if i % p == 0: 163 | prime = False 164 | break 165 | if prime: primes.append(i) 166 | pairs = set() 167 | 168 | print("Got primes") 169 | 170 | for a in primes: 171 | if primes.index(a) % 100 == 0: 172 | print("At: " + str(primes.index(a)) + " out of " + str(len(primes))) 173 | exp = 0 174 | while True: 175 | c = 0 176 | v = a + 2**exp 177 | if v > 250000: c+= 1 178 | elif v in primes: 179 | pairs.add((v,a)) 180 | v = a -2**exp 181 | if v < 1: c += 1 182 | elif v in primes: 183 | pairs.add((v,a)) 184 | if c == 2: break 185 | exp += 1 186 | 187 | print(len(pairs), len(pairs) / 2) 188 | 189 | """ 190 | --------------------------------------------------------------------------------