-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patha.py
More file actions
182 lines (140 loc) · 6.64 KB
/
Copy patha.py
File metadata and controls
182 lines (140 loc) · 6.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# https://contest.yandex.ru/contest/24810/run-report/146445411/
#
# -- Принцип работы --
#
# В данной задаче реализован алгоритм пирамидальной сортировки (heapsort) по возрастанию (неубыванию).
# Сортируемый массив может содержать элементы произвольного типа, для которого определен оператор `<`.
#
# Алгоритм состоит из следующих шагов:
#
# * Создаем пустую неубывающую бинарную кучу (min-heap).
# * По очереди вставляем все элементы массива в кучу, сохраняя ее свойства. Для этого каждый новый
# элемент кучи мы просеиваем вверх (sift up), пока куча снова не станет упорядоченной.
# * Извлекаем первый элемент из кучи и перемещаем на его место последний элемент. После этого мы
# восстанавливаем свойства кучи, выполняя просеивание нового первого элемента вниз (sift down).
# А извлеченный элемент мы добавляем в конец массива, предназначенного для хранения результата
# сортировки.
# * Повторяем предыдущую операцию до тех пор, пока куча снова не станет пустой.
#
# -- Доказательство корректности --
#
# На третьем шаге описанного выше алгоритма из кучи каждый раз извлекается элемент с минимальным
# значением. Таким образом, на выходе мы получаем массив элементов, отсортированный по возрастанию.
#
# -- Временная сложность --
#
# Временная сложность пирамидальной сортировки в среднем и худшем случаях составляет `O(n log n)`.
# В лучшем случае — когда все элементы массива равны друг другу — временная сложность сортировки
# составляет `O(n)`, поскольку при таких исходных данных просеивание кучи и вниз, и вверх каждый
# раз выполняется за константное время.
#
# -- Пространственная сложность --
#
# Пространственная сложность алгоритма составляет `O(n)`, поскольку во время его работы создается
# куча, в которую добавляются все элементы массива. А затем все элементы кучи поочередно перемещаются
# в отдельный массив, содержащий конечный результат сортировки.
from __future__ import annotations
import dataclasses
from collections.abc import Iterable, Sequence
from typing import Protocol, Self
class Comparable(Protocol):
def __lt__(self, other: Self) -> bool: ...
def heapsort[T: Comparable](array: Sequence[T]) -> Sequence[T]:
heap = Heap[T](array)
result: list[T] = []
while heap:
result.append(heap.pop())
return result
class Heap[T: Comparable]:
nodes: list[T]
def __init__(self, nodes: Iterable[T]) -> None:
self.nodes = []
for node in nodes:
self.push(node)
def __bool__(self) -> bool:
return bool(self.nodes)
def push(self, node: T) -> None:
self.nodes.append(node)
self._sift_up(len(self.nodes) - 1)
def _sift_up(self, index: int) -> None:
while True:
if index == 0:
return
parent_index = (index - 1) // 2
if not self.nodes[index] < self.nodes[parent_index]:
return
(
self.nodes[index],
self.nodes[parent_index],
) = (
self.nodes[parent_index],
self.nodes[index],
)
index = parent_index
def pop(self) -> T:
if not self.nodes:
raise ValueError('Heap is empty')
head = self.nodes[0]
self.nodes[0] = self.nodes[-1]
self.nodes.pop()
if self.nodes:
self._sift_down(0)
return head
def _sift_down(self, index: int) -> None:
last_node_index = len(self.nodes) - 1
while True:
left_child_index = index * 2 + 1
if left_child_index > last_node_index:
return
right_child_index = left_child_index + 1
smallest_child_index: int
if (
right_child_index <= last_node_index and
self.nodes[right_child_index] < self.nodes[left_child_index]
):
smallest_child_index = right_child_index
else:
smallest_child_index = left_child_index
if not self.nodes[smallest_child_index] < self.nodes[index]:
return
(
self.nodes[index],
self.nodes[smallest_child_index],
) = (
self.nodes[smallest_child_index],
self.nodes[index],
)
index = smallest_child_index
@dataclasses.dataclass(kw_only=True)
class Participant(Comparable):
name: str
score: int
penalty: int
def __lt__(self, other: Self) -> bool:
return self.get_comparison_key() < other.get_comparison_key()
def get_comparison_key(self) -> Comparable:
return (
-self.score,
self.penalty,
self.name,
)
@classmethod
def read(cls) -> Self:
fields_list = input().split()
return cls(
name=fields_list[0],
score=int(fields_list[1]),
penalty=int(fields_list[2]),
)
@classmethod
def read_list(cls, count: int) -> Iterable[Self]:
for i in range(count):
yield cls.read()
def main() -> None:
participants_count = int(input().strip())
participants = list(Participant.read_list(participants_count))
participants = list(heapsort(participants))
for participant in participants:
print(participant.name)
if __name__ == '__main__':
main()