import sys from dataclasses import dataclass # Вузол дерева: текст питання або назва тварини, лівий (так) і правий (ні) нащадки. # Uzol stromu: text otázky alebo názov zvieraťa, ľavý (áno) a pravý (nie) potomok. @dataclass class Node: text: str left: "Node | None" = None right: "Node | None" = None # Виняток для помилкової бази знань. # Výnimka pre chybnú bázu znalostí. class DatabaseError(Exception): pass # Читач рядків зі стандартного входу з підтримкою LF і CRLF. # Čítač riadkov zo štandardného vstupu s podporou LF aj CRLF. class LineReader: def __init__(self, data): # Ділимо вхід на рядки за символом LF. # Vstup rozdelíme na riadky podľa znaku LF. lines = data.split("\n") # Якщо вхід закінчується LF, останній порожній елемент не є рядком. # Ak vstup končí znakom LF, posledný prázdny prvok nie je riadok. if lines and lines[-1] == "": lines.pop() # Прибираємо лише CR з кінця (для CRLF), пробіли залишаємо. # Odstránime iba CR na konci (pre CRLF), medzery ponecháme. self.lines = [line[:-1] if line.endswith("\r") else line for line in lines] self.pos = 0 def read_line(self): # Повертає наступний рядок або None, якщо вхід закінчився. # Vráti ďalší riadok alebo None, ak sa vstup skončil. if self.pos >= len(self.lines): return None line = self.lines[self.pos] self.pos += 1 return line def is_leaf(node): # Лист — вузол без нащадків (кінцева відповідь). # List je uzol bez potomkov (konečná odpoveď). return node.left is None and node.right is None def read_node(reader): # Рекурсивно читаємо вузол і його піддерева у форматі preorder. # Rekurzívne načítame uzol a jeho podstromy vo formáte preorder. text = reader.read_line() if text is None or text == "": # Очікуваний вузол відсутній. # Chýba očakávaný uzol. raise DatabaseError() node = Node(text) if node.text.endswith("?"): # Питання має рівно двох нащадків: спершу ліве піддерево, потім праве. # Otázka má presne dvoch potomkov: najprv ľavý podstrom, potom pravý. node.left = read_node(reader) node.right = read_node(reader) return node def read_database(reader): # Читаємо все дерево і перевіряємо обов'язковий порожній рядок-роздільник. # Načítame celý strom a overíme povinný prázdny oddeľovací riadok root = read_node(reader) separator = reader.read_line() if separator is None or separator != "": # Відсутній роздільник або зайві вузли після завершення дерева. # Chýba oddeľovač alebo sú ďalšie uzly po dokončení stromu. raise DatabaseError() return root def count_questions(node): # Рекурсивно рахуємо внутрішні вузли (питання). # Rekurzívne počítame vnútorné uzly (otázky). if node is None: return 0 count = count_questions(node.left) + count_questions(node.right) if not is_leaf(node): count += 1 return count def count_answers(node): # Рекурсивно рахуємо листи (кінцеві відповіді). # Rekurzívne počítame listy (konečné odpovede). if node is None: return 0 count = count_answers(node.left) + count_answers(node.right) if is_leaf(node): count += 1 return count def search(node, reader): # Виводимо текст поточного вузла. # Vypíšeme text aktuálneho uzla. print(node.text) # Якщо знайшли відповідь, пошук завершено. # Ak sme našli odpoveď, vyhľadávanie končí. if is_leaf(node): return True # Читаємо рівно один вибір користувача. # Načítame presne jednu voľbu používateľa. choice = reader.read_line() if choice is None: # Вхід закінчився, коли очікувався вибір. # Vstup sa skončil, keď sa očakávala voľba. print("Koniec vstupu") return False if choice == "a": # Так — переходимо в ліве піддерево. # Áno — pokračujeme do ľavého podstromu. return search(node.left, reader) if choice == "n": # Ні — переходимо в праве піддерево. # Nie — pokračujeme do pravého podstromu. return search(node.right, reader) # Недійсний вибір (зокрема порожній рядок). # Neplatná voľba (vrátane prázdneho riadka). print("Nerozumiem") return False def main(): # Збільшуємо ліміт рекурсії для глибоких дерев. # Zvýšime limit rekurzie pre hlboké stromy. sys.setrecursionlimit(1000000) # Вивід у UTF-8 з LF наприкінці рядків. # Výstup v UTF-8 s LF na konci riadkov. sys.stdout.reconfigure(encoding="utf-8", newline="\n") print("Expert zo zoo to vie.") # Читаємо весь вхід як UTF-8. # Načítame celý vstup ako UTF-8. data = sys.stdin.buffer.read().decode("utf-8") reader = LineReader(data) try: root = read_database(reader) except DatabaseError: print("Chybna databaza") return # Кількості рахуємо для всієї бази, а не лише для обраного шляху. # Počty rátame pre celú bázu, nielen pre vybranú cestu. print(f"Pocet otazok: {count_questions(root)}") print(f"Pocet odpovedi: {count_answers(root)}") print("Odpovedajte 'a' pre prvu moznost alebo 'n' pre druhu moznost.") # «Koniec» виводимо лише після знайденої тварини. # «Koniec» vypíšeme iba po nájdení zvieraťa. if search(root, reader): print("Koniec") if __name__ == "__main__": main()