Update u8/program.py
This commit is contained in:
parent
c18e89e18c
commit
f53efb1050
241
u8/program.py
241
u8/program.py
@ -2,138 +2,213 @@ import sys
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
# dátová struktúra pre uzol stromu / датова структура для вузла дерева
|
# Вузол дерева: текст питання або назва тварини, лівий (так) і правий (ні) нащадки.
|
||||||
|
# Uzol stromu: text otázky alebo názov zvieraťa, ľavý (áno) a pravý (nie) potomok.
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Node:
|
class Node:
|
||||||
text: str
|
text: str
|
||||||
left: "Node | None" = None # Vetva 'a' (áno) / Гілка 'a' (так)
|
left: "Node | None" = None
|
||||||
right: "Node | None" = None # Vetva 'n' (nie) / Гілка 'n' (ні)
|
right: "Node | None" = None
|
||||||
|
|
||||||
|
|
||||||
# rekurzívne načítanie bázy znalostí (preorder)
|
# Виняток для помилкової бази знань.
|
||||||
# рекурсивне читання бази знаноь (преордер)
|
# Výnimka pre chybnú bázu znalostí.
|
||||||
|
|
||||||
def read_node(lines_iterator):
|
class DatabaseError(Exception):
|
||||||
# Rekurzívne načíta uzol a jeho podstromy vo formáte preorder.
|
pass
|
||||||
# Рекурсивно зчитує вузол та його піддерева у форматі preorder.
|
|
||||||
|
|
||||||
try:
|
|
||||||
line = next(lines_iterator)
|
|
||||||
except StopIteration:
|
|
||||||
raise ValueError("Chýba očakávaný uzol")
|
|
||||||
|
|
||||||
# Odstránime iba znaky konca riadka (\n, \r)
|
# Читач рядків зі стандартного входу з підтримкою LF і CRLF.
|
||||||
# Видаляємо лише символи кінця рядка (\n, \r)
|
# Čítač riadkov zo štandardného vstupu s podporou LF aj CRLF.
|
||||||
text = line.rstrip("\r\n")
|
|
||||||
|
|
||||||
# Kontrola prázdneho riadka na konci bázy
|
class LineReader:
|
||||||
# Перевірка порожнього рядка в кінці бази
|
def __init__(self, data):
|
||||||
if text == "":
|
|
||||||
raise ValueError("Chýba očakávaný uzol")
|
|
||||||
|
|
||||||
node = Node(text=text)
|
# Ділимо вхід на рядки за символом LF.
|
||||||
|
# Vstup rozdelíme na riadky podľa znaku LF.
|
||||||
|
|
||||||
# Ak sa text končí na '?', ide o otázku s dvoma potomkami (ľavý = áno, pravý = nie)
|
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("?"):
|
if node.text.endswith("?"):
|
||||||
node.left = read_node(lines_iterator)
|
|
||||||
node.right = read_node(lines_iterator)
|
|
||||||
|
|
||||||
|
# Питання має рівно двох нащадків: спершу ліве піддерево, потім праве.
|
||||||
|
# Otázka má presne dvoch potomkov: najprv ľavý podstrom, potom pravý.
|
||||||
|
|
||||||
|
node.left = read_node(reader)
|
||||||
|
node.right = read_node(reader)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
# rekurzívne počítanie prvkov bázy
|
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):
|
def count_questions(node):
|
||||||
# Spočíta všetky vnútorné uzly (otázky končiace znakom '?').
|
|
||||||
# Підраховує всі внутрішні вузли (питання, що закінчуються на '?').
|
# Рекурсивно рахуємо внутрішні вузли (питання).
|
||||||
|
# Rekurzívne počítame vnútorné uzly (otázky).
|
||||||
|
|
||||||
if node is None:
|
if node is None:
|
||||||
return 0
|
return 0
|
||||||
|
count = count_questions(node.left) + count_questions(node.right)
|
||||||
is_question = 1 if node.text.endswith("?") else 0
|
if not is_leaf(node):
|
||||||
return is_question + count_questions(node.left) + count_questions(node.right)
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
def count_answers(node):
|
def count_answers(node):
|
||||||
# Spočíta všetky listové uzly (konečné odpovede / zvieratá).
|
|
||||||
# Підраховує всі листові вузли (кінцеві відповіді / звірів).
|
# Рекурсивно рахуємо листи (кінцеві відповіді).
|
||||||
|
# Rekurzívne počítame listy (konečné odpovede).
|
||||||
|
|
||||||
if node is None:
|
if node is None:
|
||||||
return 0
|
return 0
|
||||||
|
count = count_answers(node.left) + count_answers(node.right)
|
||||||
if node.left is None and node.right is None:
|
if is_leaf(node):
|
||||||
return 1
|
count += 1
|
||||||
|
return count
|
||||||
return count_answers(node.left) + count_answers(node.right)
|
|
||||||
|
|
||||||
|
|
||||||
# prehľadávanie stromu a dialóg s používateľom
|
def search(node, reader):
|
||||||
# пошук у дереві та діалог з користувачем
|
|
||||||
|
|
||||||
def search(node):
|
# Виводимо текст поточного вузла.
|
||||||
# Prechádza stromom podľa volieb používateľa až po konečnú odpoveď.
|
# Vypíšeme text aktuálneho uzla.
|
||||||
# Проходить по дереву відповідно до вибору користувача до кінцевої відповіді.
|
|
||||||
|
|
||||||
# Vypíšeme text aktuálneho uzla (otázku alebo zviera)
|
|
||||||
# Виводимо текст поточного вузла (питання або звіра)
|
|
||||||
|
|
||||||
print(node.text)
|
print(node.text)
|
||||||
|
|
||||||
# Ak sme v liste, našli sme zviera - vypíšeme Koniec a skončíme
|
# Якщо знайшли відповідь, пошук завершено.
|
||||||
# Якщо ми в листі, ми знайшли звіра - виводимо Koniec і завершуємо
|
# Ak sme našli odpoveď, vyhľadávanie končí.
|
||||||
if node.left is None and node.right is None:
|
|
||||||
print("Koniec")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Načítanie voľby používateľa ('a' alebo 'n')
|
if is_leaf(node):
|
||||||
# Зчитування вибору користувача ('a' або 'n')
|
return True
|
||||||
choice_line = sys.stdin.readline()
|
|
||||||
if choice_line == "":
|
|
||||||
return # Neočakávaný koniec vstupu / Несподіваний кінець входу
|
|
||||||
|
|
||||||
choice = choice_line.rstrip("\r\n")
|
# Читаємо рівно один вибір користувача.
|
||||||
|
# Načítame presne jednu voľbu používateľa.
|
||||||
|
|
||||||
# Spracovanie voľby: 'a' ideme vľavo, 'n' ideme vpravo
|
choice = reader.read_line()
|
||||||
# Обробка вибору: 'a' перехід ліворуч, 'n' перехід праворуч
|
if choice is None:
|
||||||
if choice == 'a':
|
|
||||||
search(node.left)
|
# Вхід закінчився, коли очікувався вибір.
|
||||||
elif choice == 'n':
|
# Vstup sa skončil, keď sa očakávala voľba.
|
||||||
search(node.right)
|
|
||||||
else:
|
print("Koniec vstupu")
|
||||||
# Neplatná voľba ukončí program
|
return False
|
||||||
# Некоректний вибір завершує програму
|
if choice == "a":
|
||||||
return
|
|
||||||
|
# Так — переходимо в ліве піддерево.
|
||||||
|
# Á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():
|
def main():
|
||||||
# Požadovaný úvodný výpis
|
|
||||||
# Обов'язковий стартовий вивід
|
# Збільшуємо ліміт рекурсії для глибоких дерев.
|
||||||
|
# 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.")
|
print("Expert zo zoo to vie.")
|
||||||
|
|
||||||
# Načítanie celého vstupu do iterátora
|
# Читаємо весь вхід як UTF-8.
|
||||||
# Зчитування всього входу в ітератор
|
# Načítame celý vstup ako UTF-8.
|
||||||
lines_iterator = iter(sys.stdin.read().splitlines(keepends=True))
|
|
||||||
|
data = sys.stdin.buffer.read().decode("utf-8")
|
||||||
|
reader = LineReader(data)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
root = read_node(lines_iterator)
|
root = read_database(reader)
|
||||||
except (ValueError, StopIteration):
|
except DatabaseError:
|
||||||
|
print("Chybna databaza")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Výpočet a výpis celkových štatistík bázy
|
# Кількості рахуємо для всієї бази, а не лише для обраного шляху.
|
||||||
# Підрахунок та вивід загальної статистики бази
|
# Počty rátame pre celú bázu, nielen pre vybranú cestu.
|
||||||
q_count = count_questions(root)
|
|
||||||
a_count = count_answers(root)
|
|
||||||
|
|
||||||
print(f"Pocet otazok: {q_count}")
|
print(f"Pocet otazok: {count_questions(root)}")
|
||||||
print(f"Pocet odpovedi: {a_count}")
|
print(f"Pocet odpovedi: {count_answers(root)}")
|
||||||
print("Odpovedajte 'a' pre prvu moznost alebo 'n' pre druhu moznost.")
|
print("Odpovedajte 'a' pre prvu moznost alebo 'n' pre druhu moznost.")
|
||||||
|
|
||||||
# Spustenie dialógu od koreňa
|
# «Koniec» виводимо лише після знайденої тварини.
|
||||||
# Запуск діалогу від кореня
|
# «Koniec» vypíšeme iba po nájdení zvieraťa.
|
||||||
search(root)
|
|
||||||
|
if search(root, reader):
|
||||||
|
print("Koniec")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user