Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,24 @@

## Структура курса

| № | Тема | Секция | Папка |
|----|----------------------------------|------------------------------|-------------------|
| 1 | Введение в алгоритмы | Базовые алгоритмы | `a_intro` |
| 2 | Базовые структуры данных | Базовые алгоритмы | `b_base_ds` |
| 3 | Поиски | Базовые алгоритмы | `c_search` |
| 4 | Сортировки | Базовые алгоритмы | `d_sorting` |
| 5 | Два указателя | Методы решения | `e_two_pointers` |
| 6 | Сканирующая прямая | Методы решения | `f_scanline` |
| 7 | Разделяй и властвуй | Методы решения | `g_dnc` |
| 8 | Динамическое программирование | Методы решения | `h_dp` |
| 9 | Префиксные суммы | Продвинутые подходы | `i_prefix_sums` |
| 10 | Жадные алгоритмы | Продвинутые подходы | `j_greedy` |
| 11 | Теория чисел | Продвинутые подходы | `k_number_theory` |
| 12 | 2D Динамическое программирование | Продвинутые подходы | `l_dp2` |
| 13 | Деревья | Продвинутые структуры данных | `m_trees` |
| 14 | Кучи | Продвинутые структуры данных | `n_heaps` |
| 15 | Графы | Продвинутые структуры данных | `o_trees` |
| № | Тема | Секция | Папка |
|----|----------------------------------|------------------------------|-----------------|
| 1 | Введение в алгоритмы | Базовые алгоритмы | `intro` |
| 2 | Базовые структуры данных | Базовые алгоритмы | `base_ds` |
| 3 | Поиски | Базовые алгоритмы | `search` |
| 4 | Сортировки | Базовые алгоритмы | `sorting` |
| 5 | Два указателя | Методы решения | `two_pointers` |
| 6 | Сканирующая прямая | Методы решения | `scanline` |
| 7 | Разделяй и властвуй | Методы решения | `dnc` |
| 8 | Динамическое программирование | Методы решения | `dp` |
| 9 | Префиксные суммы | Продвинутые подходы | `prefix_sums` |
| 10 | Жадные алгоритмы | Продвинутые подходы | `greedy` |
| 11 | Теория чисел | Продвинутые подходы | `number_theory` |
| 12 | 2D Динамическое программирование | Продвинутые подходы | `dp2` |
| 13 | Деревья | Продвинутые структуры данных | `trees` |
| 14 | Кучи | Продвинутые структуры данных | `heaps` |
| 15 | Непересекающиеся множества | Продвинутые структуры данных | `dsu` |
| 16 | Графы | Продвинутые структуры данных | `graphs` |

Каждая тема содержит:

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/g_dnc/kth_largest.py → src/dnc/kth_largest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from src.d_sorting.partition import partition3
from src.sorting.partition import partition3


# O(n) в среднем, O(n^2) в худшем
Expand Down
2 changes: 1 addition & 1 deletion src/g_dnc/median_salary.py → src/dnc/median_salary.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from src.g_dnc.kth_largest import quick_select
from src.dnc.kth_largest import quick_select


# O(N) в среднем, O(N^2) в худшем
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from src.l_dp2.edit_distance import edit_distance_dp
from src.dp2.edit_distance import edit_distance_dp


# O(q * n * m), где q - количество слов, n - длина строки a, m - длина строки b
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion src/l_dp2/edit_path.py → src/dp2/edit_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from enum import StrEnum, auto
from typing import Any

from src.l_dp2.edit_distance import get_dp_table
from src.dp2.edit_distance import get_dp_table


class Operation(StrEnum):
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
141 changes: 141 additions & 0 deletions src/dsu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Задачи на непересекающиеся множества

## A. Объединение таблиц

| Поле | Значение |
|-----------|---------------------------------------------------|
| Сложность | Средняя |
| Источник | https://stepik.org/lesson/41560/step/3?unit=20013 |

В базе данных есть n таблиц, каждая содержит некоторое количество строк.
Вам нужно обрабатывать операции объединения таблиц.

Каждая таблица либо:

* содержит реальные данные (строки),
* либо является ссылкой на другую таблицу.

Изначально все таблицы содержат реальные данные.

**Операция объединения**

Для запроса (destination, source):

Найти реальную таблицу для destination (переходя по ссылкам).
Найти реальную таблицу для source.
Если это разные таблицы:

* перенести все строки из source в destination,
* сделать source ссылкой на destination,
* размер source становится 0.

Вывести максимальный размер таблицы среди всех.

**Формат входа.**

* Первая строка: n m — число таблиц и запросов
(1 ≤ n, m ≤ 100000)
* Вторая строка: r1 ... rn — размеры таблиц
(0 ≤ ri ≤ 10000)
* Далее m строк: destination source

**Формат выхода.**

Для каждого запроса выведите максимальный размер таблицы после объединения.

**Пример**

Ввод:

```text
5 5
1 1 1 1 1
3 5
2 4
1 4
5 4
5 3
```

Вывод:

```text
2
2
3
5
5
```

## B. Автоматический анализ программ

| Поле | Значение |
|-----------|---------------------------------------------------|
| Сложность | Средняя |
| Источник | https://stepik.org/lesson/41560/step/4?unit=20013 |

Дано n переменных: x1, x2, ..., xn.

Есть два типа ограничений:

* равенства: xi = xj
* неравенства: xp ≠ xq

Нужно определить, можно ли присвоить переменным целые значения так, чтобы все ограничения выполнялись.

**Формат входа**

* Первая строка: n e d
* * n — число переменных (1 ≤ n ≤ 10^5)
* * e — число равенств
* * d — число неравенств
* * (0 ≤ e, d, e + d ≤ 2⋅10^5)
* Далее e строк: i j — равенство xi = xj
* Далее d строк: i j — неравенство xi ≠ xj

**Формат выхода**

* 1 — если система выполнима
* 0 — если есть противоречие

**Пример 1**

Ввод:

```text
4 6 0
1 2
1 3
1 4
2 3
2 4
3 4
```

Вывод:

```text
1
```

**Пример 2**

Ввод:

```text
6 5 3
2 3
1 5
2 5
3 4
4 2
6 1
4 6
4 5
```

Вывод:

```text
0
```
File renamed without changes.
10 changes: 10 additions & 0 deletions src/dsu/auto_analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from src.dsu.dsu import CompressedForestDSU


def auto_analysis(n: int, e: int, d: int, equals: list[tuple[int, int]], not_equals: list[tuple[int, int]]) -> bool:
s = CompressedForestDSU(n)
for i in range(1, n + 1):
s.make_set(i)
for i in range(e):
s.union(equals[i][0], equals[i][1])
return all(s.find(not_equals[i][0]) != s.find(not_equals[i][1]) for i in range(d))
153 changes: 153 additions & 0 deletions src/dsu/dsu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
from abc import ABC, abstractmethod


class DSU(ABC):
def __init__(self, n: int) -> None:
self.n = n

@abstractmethod
def make_set(self, x: int) -> None:
"""Создать одноэлементное множество {x}"""
...

@abstractmethod
def find(self, x: int) -> int:
"""Выдать ID множества, содержащего {x}"""
...

@abstractmethod
def union(self, x: int, y: int) -> None:
"""Объединить множества, содержащие {x} и {y}"""
...


class ArrayDSU(DSU):
"""
Система непересекающихся множеств на массиве.

ID множества - минимальный элемент в нем.

s1 = {9, 3, 2, 4, 7}, s2 = {5}, s3 = {6, 1, 8}

smallest[i] - ID множества, которому принадлежит элемент i:
smallest[9] = 2, smallest[3] = 2, smallest[2] = 2, smallest[4] = 2, smallest[7] = 2, smallest[5] = 5,
smallest[6] = 1, smallest[1] = 1, smallest[8] = 1.

smallest = [0, 1, 2, 2, 2, 5, 1, 2, 1, 2]
"""

def __init__(self, n: int) -> None:
super().__init__(n)
self.smallest = [0] * (self.n + 1)

# O(1)
def make_set(self, x: int) -> None:
self.smallest[x] = x

# O(1)
def find(self, x: int) -> int:
return self.smallest[x]

# O(N)
def union(self, x: int, y: int) -> None:
x_id = self.find(x)
y_id = self.find(y)
if x_id == y_id:
return
new_id = min(x_id, y_id)
for i in range(1, self.n + 1):
if self.smallest[i] in {x_id, y_id}:
self.smallest[i] = new_id


class ForestDSU(DSU):
"""
Система непересекающихся множеств на лесе корневых деревьев.

ID множества - корень дерева.

s1 = {9, 3, 2, 4, 7}, s2 = {5}, s3 = {6, 1, 8}

parent[i] - родитель элемента i в дереве.

union(9, 3) : ранги равны => подвешиваем 9 к 3
union(3, 2) : rank[3] > rank[2] => подвешиваем 2 к 3
union(2, 4) : rank[2] = rank[3] > rank[4] => подвешиваем 4 к 3
union(4, 7) : rank[4] = rank[3] > rank[7] => подвешиваем 7 к 3
union(6, 1) : ранги равны => подвешиваем 6 к 1
union(1, 8) : rank[1] > rank[8] => подвешиваем 8 к 1

Множество s1 (корень 3):
3
/ | | \
9 2 4 7

Множество s2 (корень 5):
5

Множество s3 (корень 1):
1
/ \
6 8

parent = [0, 1, 3, 3, 3, 5, 1, 3, 1, 3]

rank[i] - ранг элемента i в дереве, который используется при объединении.
В текущей реализации ранг равен высоте дерева в элементе.

rank = [0, 1, 0, 1, 0, 0, 0, 0, 0, 0]
"""

def __init__(self, n: int) -> None:
super().__init__(n)
self.parent = [0] * (self.n + 1)
self.rank = [0] * (self.n + 1)

# O(1)
def make_set(self, x: int) -> None:
self.parent[x] = x
self.rank[x] = 0

# O(log N)
def find(self, x: int) -> int:
while x != self.parent[x]:
x = self.parent[x]
return self.parent[x]

# O(log N)
def union(self, x: int, y: int) -> None:
x_root = self.find(x)
y_root = self.find(y)
if x_root == y_root:
return
# Более низкое подвешиваем к более высокому, чтобы не увеличивать глубину дерева
if self.rank[x_root] > self.rank[y_root]:
self.parent[y_root] = x_root
else:
self.parent[x_root] = y_root

# При одинаковой высоте деревьев подвешиваем к одному из них, а его высоту увеличиваем на единицу
if self.rank[x_root] == self.rank[y_root]:
self.rank[y_root] += 1


class CompressedForestDSU(ForestDSU):
"""
Система непересекающихся множеств на лесе корневых деревьев с использованием сжатия путей.
ID множества - корень дерева.

Отличия от ForestDSU:
- При поиске корня дерева для элемента i, мы не только находим корень, но и "подвешиваем" все элементы на пути от i
до корня непосредственно к корню.
- Это позволяет значительно ускорить последующие операции find для этих элементов, так как они будут находиться на
уровне 1 от корня.

Побочный эффект:
- rank[i] перестает хранить точную высоту элемента i в дереве, а ставится верхней оценкой все еще актуальной при
объединении.
"""

def find(self, x: int) -> int:
if x != self.parent[x]:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
Loading
Loading