add theme 1.5, 1.6
Родитель:
50dfea9
· сравнить с родителем
8 файлов
-
Показать разницуСкрыть разницу
1 +# ⭐ Необязательные задачи 2 + 3 +> Дополнительная тренировка по теме. Решайте, если обязательные задачи уже сданы. 4 + 5 +--- 6 + 7 +## Задача 1. Совершенное число 8 + 9 +### Условие 10 + 11 +Число называется **совершенным**, если оно равно сумме всех своих делителей, кроме самого себя. Например, `6 = 1 + 2 + 3`. 12 + 13 +Напишите функцию `int sumDivisors(int n)`, которая возвращает сумму собственных делителей числа, и с её помощью определите, является ли число совершенным. 14 + 15 +### Входные данные 16 + 17 +Одно натуральное число `n` (`n ≤ 10⁶`). 18 + 19 +### Выходные данные 20 + 21 +`YES`, если число совершенное, иначе `NO`. 22 + 23 +### Примеры 24 + 25 +| Ввод | Вывод | 26 +|---|---| 27 +| `6` | `YES` | 28 +| `28` | `YES` | 29 +| `12` | `NO` | 30 + 31 +> [!NOTE] 32 +> Делители 28: 1, 2, 4, 7, 14 — их сумма 28. Делители 12: 1, 2, 3, 4, 6 — их сумма 16. У числа 1 собственных делителей нет, сумма равна 0, поэтому 1 не совершенное. 33 + 34 +--- 35 + 36 +## Задача 2. Цифровой корень 37 + 38 +### Условие 39 + 40 +**Цифровой корень** числа получается так: складываем цифры числа, потом цифры результата, и так далее, пока не останется одна цифра. 41 + 42 +Напишите рекурсивную функцию `int digitSum(int n)` для суммы цифр и рекурсивную функцию `int digitalRoot(int n)` для цифрового корня. 43 + 44 +### Входные данные 45 + 46 +Одно натуральное число `n`. 47 + 48 +### Выходные данные 49 + 50 +Одно число — цифровой корень. 51 + 52 +### Примеры 53 + 54 +| Ввод | Вывод | 55 +|---|---| 56 +| `942` | `6` | 57 +| `7` | `7` | 58 +| `99999` | `9` | 59 + 60 +> [!TIP] 61 +> `942 → 9 + 4 + 2 = 15 → 1 + 5 = 6`. База — `n < 10`, рекурсивный шаг — `digitalRoot(digitSum(n))`. Одна функция вызывает другую — это и есть композиция функций. 62 + 63 +--- 64 + 65 +## Задача 3. Последовательность Коллатца 66 + 67 +### Условие 68 + 69 +Возьмём натуральное число `n`. Если оно чётное, делим его на 2, если нечётное — заменяем на `3n + 1`. Повторяем, пока не получим 1. 70 + 71 +Напишите **рекурсивную** функцию `int steps(int n)`, которая возвращает количество шагов до получения единицы. 72 + 73 +### Входные данные 74 + 75 +Одно натуральное число `n` (`n ≤ 10⁶`). 76 + 77 +### Выходные данные 78 + 79 +Одно число — количество шагов. 80 + 81 +### Примеры 82 + 83 +| Ввод | Вывод | 84 +|---|---| 85 +| `6` | `8` | 86 +| `1` | `0` | 87 +| `27` | `111` | 88 + 89 +> [!NOTE] 90 +> Для 6: `6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1` — восемь шагов. База — `n == 1` (шагов не нужно), рекурсивный шаг — `1 + steps(n / 2)` для чётного и `1 + steps(3 * n + 1)` для нечётного. Промежуточные значения могут быть больше `n`, поэтому для больших чисел безопаснее `long long`. 91 + 92 +--- 93 + 94 +## Задача 4. Минимальный делитель 95 + 96 +### Условие 97 + 98 +Напишите функцию `int minDivisor(int n)`, которая возвращает наименьший делитель числа `n`, больший 1. 99 + 100 +### Входные данные 101 + 102 +Одно натуральное число `n` (`2 ≤ n ≤ 2·10⁹`). 103 + 104 +### Выходные данные 105 + 106 +Одно число — минимальный делитель. 107 + 108 +### Примеры 109 + 110 +| Ввод | Вывод | 111 +|---|---| 112 +| `15` | `3` | 113 +| `16` | `2` | 114 +| `17` | `17` | 115 + 116 +> [!TIP] 117 +> Проверяйте делители, пока `i * i <= n`. Если делитель не нашёлся, число простое и ответ — само `n`. Для `n` около 2·10⁹ произведение `i * i` может не поместиться в `int` — объявите `i` как `long long`. 118 + 119 +--- 120 + 121 +## Задача 5. Синус через ряд Тейлора 122 + 123 +### Условие 124 + 125 +Вычислите `sin(x)` с помощью ряда Тейлора: 126 + 127 +```text 128 +sin(x) = x - x³/3! + x⁵/5! - x⁷/7! + ... 129 +``` 130 + 131 +Суммируйте члены ряда, пока модуль очередного члена больше `10⁻⁶`. Оформите вычисление в виде функции `double taylorSine(double x)`. Пользоваться функцией `sin` из `<cmath>` нельзя. 132 + 133 +### Входные данные 134 + 135 +Одно вещественное число `x` (`|x| ≤ 10`) — угол в радианах. 136 + 137 +### Выходные данные 138 + 139 +Значение `sin(x)` с шестью знаками после запятой. 140 + 141 +### Примеры 142 + 143 +| Ввод | Вывод | 144 +|---|---| 145 +| `0` | `0.000000` | 146 +| `1.5707963` | `1.000000` | 147 +| `1` | `0.841471` | 148 + 149 +> [!TIP] 150 +> Не считайте факториал и степень заново для каждого члена. Следующий член получается из предыдущего: `term = -term * x * x / ((2 * n) * (2 * n + 1))`, где `n` — номер шага. Для вывода используйте `fixed << setprecision(6)`. -
Показать разницуСкрыть разницу
1 +# ✅ Обязательные задачи (LMS) 2 + 3 +> Эти задачи нужно решить и сдать в LMS. 4 + 5 +--- 6 + 7 +## Задача 1. Ханойские башни 8 + 9 +### Условие 10 + 11 +Головоломка «Ханойские башни» состоит из трёх стержней и `N` дисков разного размера. В начале все диски надеты на первый стержень в порядке убывания: самый большой внизу, самый маленький наверху. Нужно перенести всю пирамиду на третий стержень, соблюдая два правила: 12 + 13 +- за один ход перекладывается ровно один диск; 14 +- больший диск нельзя класть поверх меньшего. 15 + 16 +Второй стержень можно использовать как вспомогательный. Определите, за какое наименьшее количество ходов это можно сделать. 17 + 18 +Задачу нужно решить **рекурсивно**: реализуйте функцию `int hanoi(int n)`, которая возвращает минимальное число ходов. 19 + 20 +### Входные данные 21 + 22 +Одно натуральное число `N` — количество дисков (`1 ≤ N ≤ 30`). 23 + 24 +### Выходные данные 25 + 26 +Одно число — минимальное количество ходов. 27 + 28 +### Пример 29 + 30 +**Ввод:** 31 + 32 +```text 33 +3 34 +``` 35 + 36 +**Вывод:** 37 + 38 +```text 39 +7 40 +``` 41 + 42 +> [!TIP] 43 +> Чтобы перенести `N` дисков, нужно перенести `N − 1` дисков на вспомогательный стержень, переложить самый большой диск и снова перенести `N − 1` дисков поверх него. Отсюда `hanoi(n) = 2 * hanoi(n - 1) + 1`, а база — `hanoi(1) = 1`. 44 + 45 +--- 46 + 47 +## Задача 2. Количество разменов 48 + 49 +### Условие 50 + 51 +Даны три номинала монет `a`, `b`, `c` и сумма `S`. Определите, сколькими способами можно набрать сумму `S` этими монетами. Каждую монету можно использовать любое количество раз, порядок монет не важен. 52 + 53 +Напишите функцию `int count_ways(int a, int b, int c, int S)`, которая возвращает количество способов. 54 + 55 +### Входные данные 56 + 57 +Четыре натуральных числа `a`, `b`, `c` и `S`, записанных через пробел. 58 + 59 +### Выходные данные 60 + 61 +Одно число — количество способов размена. 62 + 63 +### Пример 64 + 65 +**Ввод:** 66 + 67 +```text 68 +1 2 5 5 69 +``` 70 + 71 +**Вывод:** 72 + 73 +```text 74 +4 75 +``` 76 + 77 +> [!NOTE] 78 +> Способы набрать 5: `5`, `1 + 2 + 2`, `1 + 1 + 1 + 2`, `1 + 1 + 1 + 1 + 1`. Перебирайте вложенными циклами количество монет номинала `a` и `b`, а остаток проверяйте на делимость на `c`: `(S - i*a - j*b) % c == 0`. 79 + 80 +--- 81 + 82 +## Задача 3. Наибольший общий делитель 83 + 84 +### Условие 85 + 86 +Напишите **рекурсивную** функцию `int gcd(int a, int b)`, которая вычисляет НОД двух чисел по алгоритму Евклида. С её помощью найдите НОД трёх чисел. 87 + 88 +### Входные данные 89 + 90 +Три натуральных числа. 91 + 92 +### Выходные данные 93 + 94 +Одно число — их наибольший общий делитель. 95 + 96 +### Пример 97 + 98 +**Ввод:** 99 + 100 +```text 101 +36 48 60 102 +``` 103 + 104 +**Вывод:** 105 + 106 +```text 107 +12 108 +``` 109 + 110 +> [!TIP] 111 +> Алгоритм Евклида: `gcd(a, b) = gcd(b, a % b)`, база — `gcd(a, 0) = a`. Для трёх чисел используйте вложенный вызов: `gcd(gcd(a, b), c)`. 112 + 113 +--- 114 + 115 +## Задача 4. Синхронизация времени (SNTP) 116 + 117 +### Условие 118 + 119 +Клиент отправляет запрос на сервер и запоминает время отправления `A`. Сервер получает запрос в момент `B` (по часам сервера) и сразу отправляет клиенту ответ, в котором указано время `B`. Клиент получает ответ в момент `C` (по своим часам). Задержки в сети туда и обратно одинаковы. 120 + 121 +Определите точное время, которое нужно установить на часах клиента в момент получения ответа. 122 + 123 +### Входные данные 124 + 125 +Три строки — временные метки `A`, `B` и `C` в формате `hh:mm:ss`. 126 + 127 +### Выходные данные 128 + 129 +Точное время в формате `hh:mm:ss`. 130 + 131 +### Пример 132 + 133 +**Ввод:** 134 + 135 +```text 136 +15:01:00 137 +18:09:45 138 +15:01:40 139 +``` 140 + 141 +**Вывод:** 142 + 143 +```text 144 +18:10:05 145 +``` 146 + 147 +> [!NOTE] 148 +> Запрос шёл туда и обратно `C − A = 40` секунд, значит, в одну сторону — 20 секунд. Точное время: `B + (C − A) / 2 = 18:09:45 + 20 = 18:10:05`. 149 + 150 +> [!TIP] 151 +> - Считывайте время с разделителями: `cin >> h >> sep >> m >> sep >> s;`, где `char sep`. 152 +> - Вынесите перевод в секунды в функцию `int toSeconds(int h, int m, int s)`. 153 +> - Время может перейти через полночь, поэтому используйте `(x + 86400) % 86400`. 154 +> - Для ведущих нулей: `setfill('0') << setw(2)` из `<iomanip>`. 155 + 156 +--- 157 + 158 +## Задача 5. Бизнес-центр 159 + 160 +### Условие 161 + 162 +Бизнес-центр — `N`-этажное здание. На каждом этаже работает ровно один сотрудник. Утром все сотрудники приезжают на парковку в подвале (на один этаж ниже первого). 163 + 164 +Лифт один раз отвозит всех желающих на выбранный этаж `X`, тратя `C` секунд на каждый этаж пути. Дальше каждый сотрудник добирается до своего этажа пешком: подъём на один этаж занимает `A` секунд, спуск — `B` секунд. Сотрудник может и не пользоваться лифтом, а сразу пойти пешком вверх с парковки. 165 + 166 +Найдите минимальное суммарное время: время работы лифта плюс время, которое все сотрудники тратят на дорогу. 167 + 168 +### Входные данные 169 + 170 +Четыре натуральных числа `N`, `A`, `B`, `C`. 171 + 172 +### Выходные данные 173 + 174 +Одно число — минимальное время. 175 + 176 +### Пример 177 + 178 +**Ввод:** 179 + 180 +```text 181 +3 2 1 1 182 +``` 183 + 184 +**Вывод:** 185 + 186 +```text 187 +5 188 +``` 189 + 190 +> [!NOTE] 191 +> Если лифт едет на 2-й этаж: лифт тратит `2 · 1 = 2` секунды, сотрудник 1-го этажа спускается за 1 секунду, сотрудник 2-го уже на месте, сотрудник 3-го поднимается за 2 секунды. Итого `2 + 1 + 0 + 2 = 5`. 192 + 193 +> [!TIP] 194 +> Переберите этаж `X` от 1 до `N`. Для каждого сотрудника `i` выберите лучшее из двух: пешком с парковки (`i * A`) или от этажа `X` на лифте. Используйте `long long`. 195 + 196 +--- 197 + 198 +## Задача 6. Числа Фибоначчи без циклов 199 + 200 +### Условие 201 + 202 +Последовательность Фибоначчи задаётся так: `a₀ = 0`, `a₁ = 1`, `aₖ = aₖ₋₁ + aₖ₋₂` при `k > 1`. 203 + 204 +Найдите `N`-е число Фибоначчи. **В программе запрещается использовать циклы.** 205 + 206 +### Входные данные 207 + 208 +Одно целое неотрицательное число `N` (`N ≤ 30`). 209 + 210 +### Выходные данные 211 + 212 +Одно число — `N`-е число Фибоначчи. 213 + 214 +### Пример 215 + 216 +**Ввод:** 217 + 218 +```text 219 +7 220 +``` 221 + 222 +**Вывод:** 223 + 224 +```text 225 +13 226 +``` 227 + 228 +> [!NOTE] 229 +> Первые числа последовательности: 0, 1, 1, 2, 3, 5, 8, 13, ... Под номером 7 (считая с нуля) стоит 13. У рекурсивной функции две базы: `n == 0` и `n == 1`. -
Показать разницуСкрыть разницу
1 +# 🧑🏫 Практика 2 + 3 +> Задачи, которые решаем на занятиях. 4 + 5 +--- 6 + 7 +## Задача 1. Сумма цифр числа 8 + 9 +### Условие 10 + 11 +Дано натуральное число `n`. Найдите сумму его цифр, используя **рекурсивную** функцию `int sumDigits(int n)`. 12 + 13 +### Входные данные 14 + 15 +Одно натуральное число `n`. 16 + 17 +### Выходные данные 18 + 19 +Одно число — сумма цифр. 20 + 21 +### Примеры 22 + 23 +**Ввод:** 24 + 25 +```text 26 +1234 27 +``` 28 + 29 +**Вывод:** 30 + 31 +```text 32 +10 33 +``` 34 + 35 +**Ввод:** 36 + 37 +```text 38 +507 39 +``` 40 + 41 +**Вывод:** 42 + 43 +```text 44 +12 45 +``` 46 + 47 +> [!NOTE] 48 +> База рекурсии — однозначное число (`n < 10`): сумма его цифр равна ему самому. Рекурсивный шаг — последняя цифра `n % 10` плюс сумма цифр числа без неё `sumDigits(n / 10)`. 49 +> 50 +> ```text 51 +> sumDigits(1234) = 4 + sumDigits(123) 52 +> = 4 + 3 + sumDigits(12) 53 +> = 4 + 3 + 2 + sumDigits(1) 54 +> = 4 + 3 + 2 + 1 = 10 55 +> ``` 56 + 57 +--- 58 + 59 +## Задача 2. Простые близнецы 60 + 61 +### Условие 62 + 63 +Даны два натуральных числа. Напишите функцию `bool isPrime(int n)`, которая определяет, является ли число простым. С её помощью определите, являются ли данные числа **простыми близнецами** — простыми числами, которые отличаются на 2. 64 + 65 +### Входные данные 66 + 67 +Два натуральных числа через пробел. 68 + 69 +### Выходные данные 70 + 71 +`YES`, если числа — простые близнецы, иначе `NO`. 72 + 73 +### Примеры 74 + 75 +**Ввод:** 76 + 77 +```text 78 +17 19 79 +``` 80 + 81 +**Вывод:** 82 + 83 +```text 84 +YES 85 +``` 86 + 87 +**Ввод:** 88 + 89 +```text 90 +17 20 91 +``` 92 + 93 +**Вывод:** 94 + 95 +```text 96 +NO 97 +``` 98 + 99 +> [!NOTE] 100 +> Числа 17 и 19 простые, и `19 − 17 = 2`. Число 20 составное, поэтому во втором примере ответ `NO`. Не забудьте, что 0 и 1 простыми не являются, а делители достаточно проверять, пока `i * i <= n`. 101 + 102 +--- 103 + 104 +## Задача 3. Симметричное число 105 + 106 +### Условие 107 + 108 +Напишите **рекурсивную** функцию `int reverseNumber(int n)`, которая возвращает число, записанное теми же цифрами в обратном порядке. С её помощью определите, является ли число палиндромом. 109 + 110 +### Входные данные 111 + 112 +Одно натуральное число. 113 + 114 +### Выходные данные 115 + 116 +`YES`, если число — палиндром, иначе `NO`. 117 + 118 +### Примеры 119 + 120 +**Ввод:** 121 + 122 +```text 123 +12321 124 +``` 125 + 126 +**Вывод:** 127 + 128 +```text 129 +YES 130 +``` 131 + 132 +**Ввод:** 133 + 134 +```text 135 +1231 136 +``` 137 + 138 +**Вывод:** 139 + 140 +```text 141 +NO 142 +``` 143 + 144 +> [!TIP] 145 +> Удобно добавить второй параметр со значением по умолчанию: `int reverseNumber(int n, int reversed = 0)`. На каждом шаге последняя цифра переносится в `reversed`: `reverseNumber(n / 10, reversed * 10 + n % 10)`. База — `n == 0`, тогда ответ уже собран в `reversed`. 146 + 147 +--- 148 + 149 +## Задача 4. Наименьшее общее кратное 150 + 151 +### Условие 152 + 153 +Напишите рекурсивную функцию `int gcd(int a, int b)` для НОД по алгоритму Евклида и функцию `long long lcm(long long a, long long b)`, которая с её помощью вычисляет НОК двух чисел. Найдите НОК трёх чисел. 154 + 155 +### Входные данные 156 + 157 +Три натуральных числа, каждое не больше 1000. 158 + 159 +### Выходные данные 160 + 161 +Одно число — наименьшее общее кратное. 162 + 163 +### Пример 164 + 165 +**Ввод:** 166 + 167 +```text 168 +4 6 10 169 +``` 170 + 171 +**Вывод:** 172 + 173 +```text 174 +60 175 +``` 176 + 177 +> [!NOTE] 178 +> НОК выражается через НОД: `lcm(a, b) = a * b / gcd(a, b)`. Для трёх чисел — `lcm(lcm(a, b), c)`. Здесь `lcm(4, 6) = 12`, а `lcm(12, 10) = 120 / 2 = 60`. Произведение `a * b` быстро растёт, поэтому НОК считаем в `long long`. 179 + 180 +--- 181 + 182 +## Задача 5. Башни кубиков 183 + 184 +### Условие 185 + 186 +Даны высоты трёх башен. Напишите функции `int max3(int a, int b, int c)` и `int min3(int a, int b, int c)` и с их помощью найдите разницу между самой высокой и самой низкой башней. 187 + 188 +### Входные данные 189 + 190 +Три целых числа через пробел. 191 + 192 +### Выходные данные 193 + 194 +Одно число — разница между максимумом и минимумом. 195 + 196 +### Пример 197 + 198 +**Ввод:** 199 + 200 +```text 201 +18 25 13 202 +``` 203 + 204 +**Вывод:** 205 + 206 +```text 207 +12 208 +``` 209 + 210 +> [!NOTE] 211 +> Самая высокая башня — 25, самая низкая — 13, разница `25 − 13 = 12`. Внутри `max3` и `min3` начинайте с первого числа (`int m = a;`), а не с нуля — иначе функция ошибётся на отрицательных числах. -
Показать разницуСкрыть разницу
1 +# 📘 Тема 1.5. Необходимый материал 2 + 3 +> Шпаргалка по функциям, декомпозиции и рекурсии. 4 + 5 +--- 6 + 7 +## 1. Что такое функция 8 + 9 +**Функция** — именованный блок кода, который выполняет одну задачу и может быть вызван из других частей программы. 10 + 11 +Зачем нужны функции: 12 + 13 +- **Повторное использование.** Код пишется один раз, а вызывается сколько угодно раз. 14 +- **Модульность.** Программа разбивается на логические блоки, её проще читать и отлаживать. 15 +- **Одно место для изменений.** Если формула поменялась, правим её в одной функции, а не в десяти местах. 16 + 17 +**Декомпозиция** — разбиение большой задачи на небольшие подзадачи, каждая из которых оформляется отдельной функцией. 18 + 19 +Термины: 20 + 21 +| Термин | Значение | 22 +|---|---| 23 +| **параметр** | переменная в заголовке функции, через которую она получает данные (`int x`) | 24 +| **аргумент** | конкретное значение, которое передаётся при вызове (`square(5)` — аргумент `5`) | 25 +| **возвращаемое значение** | результат, который функция отдаёт через `return` | 26 +| **локальная переменная** | переменная, объявленная внутри функции; снаружи её не видно | 27 +| **прототип** | объявление функции без тела | 28 + 29 +> [!NOTE] 30 +> `main` — тоже функция. Она возвращает `int`, и `return 0;` в конце сообщает системе, что программа завершилась успешно. 31 + 32 +--- 33 + 34 +## 2. Синтаксис функции 35 + 36 +```cpp 37 +тип имя_функции(параметры) { 38 + // инструкции 39 + return значение; 40 +} 41 +``` 42 + 43 +- **тип** — тип возвращаемого значения (`int`, `long long`, `double`, `bool`, `string`). Если функция ничего не возвращает — `void`. 44 +- **имя** — пишется с маленькой буквы по правилам имён переменных. 45 +- **параметры** — входные данные через запятую, у каждого свой тип. 46 +- **`return`** — возвращает значение и **сразу завершает** функцию. 47 + 48 +```cpp 49 +int square(int x) { 50 + return x * x; 51 +} 52 + 53 +int main() { 54 + cout << square(3) << endl; // 9 55 + cout << square(square(2)) << endl; // 16 56 + int y = square(4) + 1; // 17 57 + return 0; 58 +} 59 +``` 60 + 61 +> [!TIP] 62 +> Функция — это «чёрный ящик»: даём ей вход, получаем выход. Результат можно вывести, сохранить в переменную или подставить в другое выражение, в том числе в вызов другой функции. 63 + 64 +--- 65 + 66 +## 3. Виды подпрограмм 67 + 68 +| Вид | Что делает | Пример | 69 +|---|---|---| 70 +| **процедура** (`void`) | выполняет действие, ничего не возвращает | `void printHello()` | 71 +| **функция** | вычисляет и возвращает значение | `int square(int x)` | 72 +| **рекурсивная функция** | вызывает сама себя | `int factorial(int n)` | 73 + 74 +```cpp 75 +// процедура 76 +void printHello() { 77 + cout << "Hello!" << endl; 78 +} 79 + 80 +// функция 81 +int square(int x) { 82 + return x * x; 83 +} 84 + 85 +// рекурсия 86 +int factorial(int n) { 87 + if (n <= 1) return 1; 88 + return n * factorial(n - 1); 89 +} 90 +``` 91 + 92 +--- 93 + 94 +## 4. Прототип функции 95 + 96 +Функцию нужно объявить **до** того места, где она вызывается. Если хочется написать её после `main`, сверху ставят **прототип** — объявление без тела. 97 + 98 +```cpp 99 +#include <iostream> 100 +using namespace std; 101 + 102 +int square(int x); // прототип 103 + 104 +int main() { 105 + cout << square(5) << endl; 106 + return 0; 107 +} 108 + 109 +int square(int x) { // определение 110 + return x * x; 111 +} 112 +``` 113 + 114 +> [!WARNING] 115 +> Без прототипа такой код не скомпилируется: компилятор читает файл сверху вниз и в `main` ещё не знает, что такое `square`. 116 + 117 +--- 118 + 119 +## 5. Передача параметров по значению 120 + 121 +Функция получает **копию** аргумента. Изменения внутри функции на оригинал не влияют. 122 + 123 +```cpp 124 +void square(int m) { 125 + m = m * m; 126 + cout << "In square: m = " << m << endl; 127 +} 128 + 129 +int main() { 130 + int n = 4; 131 + cout << "Before: n = " << n << endl; 132 + square(n); 133 + cout << "After: n = " << n << endl; 134 + return 0; 135 +} 136 +``` 137 + 138 +```text 139 +Before: n = 4 140 +In square: m = 16 141 +After: n = 4 142 +``` 143 + 144 +Здесь `m` — копия `n`. Изменение `m` не меняет `n`. Чтобы функция изменила оригинал, нужна передача по ссылке или указателю — к этому мы ещё вернёмся. 145 + 146 +### Значение параметра по умолчанию 147 + 148 +Параметру можно задать значение по умолчанию — тогда при вызове его можно не указывать. 149 + 150 +```cpp 151 +int reverseNumber(int n, int reversed = 0) { ... } 152 + 153 +reverseNumber(123); // reversed = 0 154 +reverseNumber(123, 5); // reversed = 5 155 +``` 156 + 157 +--- 158 + 159 +## 6. Возврат указателя 160 + 161 +Если функция возвращает указатель, он должен указывать на память, которая **жива после выхода** из функции, либо быть равным `nullptr`. 162 + 163 +```cpp 164 +// ❌ ошибка: a и b — локальные копии, после выхода из функции их нет 165 +int* maxPtr(int a, int b) { 166 + if (a > b) return &a; 167 + return &b; 168 +} 169 +``` 170 + 171 +```cpp 172 +// ✅ указатели ссылаются на переменные из main 173 +int* maxPtr(int* a, int* b) { 174 + if (*a > *b) return a; 175 + return b; 176 +} 177 + 178 +int main() { 179 + int n = 5, m = 4; 180 + int* p = maxPtr(&n, &m); 181 + cout << *p << endl; // 5 182 + return 0; 183 +} 184 +``` 185 + 186 +> [!CAUTION] 187 +> Указатель на локальную переменную называют **висячим**: он показывает на память, которая уже освобождена. Программа может выдать мусор или упасть. 188 + 189 +--- 190 + 191 +## 7. Декомпозиция: пример с рядом Тейлора 192 + 193 +Задача: вычислить `sin(x)` через ряд 194 + 195 +```text 196 +sin(x) = x - x³/3! + x⁵/5! - x⁷/7! + ... 197 +``` 198 + 199 +Суммируем члены, пока очередной член не станет меньше точности `10⁻⁶`. 200 + 201 +Каждый следующий член получается из предыдущего: `term = -term * x * x / ((2n) * (2n + 1))`. Поэтому факториал в явном виде считать не нужно. 202 + 203 +```cpp 204 +#include <iostream> 205 +#include <cmath> 206 +using namespace std; 207 + 208 +double taylorSine(double x, double epsilon = 1e-6) { 209 + double result = 0.0; 210 + double term = x; 211 + int n = 1; 212 + while (fabs(term) > epsilon) { 213 + result += term; 214 + term = -term * x * x / ((2 * n) * (2 * n + 1)); 215 + n++; 216 + } 217 + return result; 218 +} 219 + 220 +int main() { 221 + double x; 222 + cin >> x; 223 + cout << "Taylor: " << taylorSine(x) << endl; 224 + cout << "Library: " << sin(x) << endl; 225 + return 0; 226 +} 227 +``` 228 + 229 +> [!TIP] 230 +> Признаки хорошей декомпозиции: `main` короткий и читается как план решения, каждая функция делает одну вещь, каждую функцию можно проверить отдельно. 231 + 232 +--- 233 + 234 +## 8. Рекурсия 235 + 236 +**Рекурсия** — приём, при котором функция вызывает саму себя для задачи меньшего размера. 237 + 238 +У рекурсивной функции всегда две части: 239 + 240 +1. **База рекурсии** — условие, при котором функция возвращает ответ сразу, без вызова себя. 241 +2. **Рекурсивный шаг** — вызов функции с аргументом, который приближает к базе. 242 + 243 +```cpp 244 +int factorial(int n) { 245 + if (n <= 1) { 246 + return 1; // база 247 + } 248 + return n * factorial(n - 1); // рекурсивный шаг 249 +} 250 +``` 251 + 252 +Как это работает для `n = 5`: 253 + 254 +```text 255 +factorial(5) 256 += 5 * factorial(4) 257 += 5 * 4 * factorial(3) 258 += 5 * 4 * 3 * factorial(2) 259 += 5 * 4 * 3 * 2 * factorial(1) 260 += 5 * 4 * 3 * 2 * 1 261 += 120 262 +``` 263 + 264 +Каждый вызов ждёт результата следующего и хранится в **стеке вызовов**. Когда достигнута база, результаты начинают «возвращаться» вверх. 265 + 266 +> [!CAUTION] 267 +> Без базы (или если база никогда не достигается) функция вызывает себя бесконечно. Стек вызовов заканчивается, и программа падает — это **переполнение стека**. 268 + 269 +--- 270 + 271 +## 9. Последовательности и рекуррентные формулы 272 + 273 +**Последовательность** — упорядоченный набор чисел `a₁, a₂, a₃, ...`, где `n` — номер члена. 274 + 275 +| Последовательность | Формула n-го члена | Пример | 276 +|---|---|---| 277 +| арифметическая прогрессия | `aₙ = a₁ + (n − 1) · d` | `a₁ = 2, d = 3` → 2, 5, 8, 11, ... | 278 +| геометрическая прогрессия | `aₙ = a₁ · r^(n − 1)` | `a₁ = 2, r = 3` → 2, 6, 18, 54, ... | 279 + 280 +**Рекуррентная формула** выражает член последовательности через предыдущие: `aₙ = f(aₙ₋₁, aₙ₋₂, ...)`. 281 + 282 +| Последовательность | Начальные условия | Рекуррентная формула | 283 +|---|---|---| 284 +| факториал | `0! = 1` | `n! = n · (n − 1)!` | 285 +| Фибоначчи | `F(0) = 0`, `F(1) = 1` | `F(n) = F(n − 1) + F(n − 2)` | 286 +| член ряда Тейлора для `sin` | `T₀ = x` | `Tₙ = −Tₙ₋₁ · x² / ((2n)(2n + 1))` | 287 +| НОД (алгоритм Евклида) | `gcd(a, 0) = a` | `gcd(a, b) = gcd(b, a % b)` | 288 +| Ханойские башни | `h(1) = 1` | `h(n) = 2 · h(n − 1) + 1` | 289 + 290 +### Как перевести последовательность в рекуррентную формулу 291 + 292 +1. Найдите закономерность: как следующий член связан с предыдущими. 293 +2. Выразите `aₙ` через `aₙ₋₁`, `aₙ₋₂`, ... 294 +3. Запишите начальные условия — они станут **базой** рекурсии. 295 +4. Проверьте формулу на нескольких первых членах. 296 + 297 +Пример: 1, 1, 2, 6, 24, 120, ... — каждый член в `n` раз больше предыдущего, значит, `aₙ = n · aₙ₋₁`, `a₀ = 1`. Проверка: `a₃ = 3 · 2 = 6` ✅. 298 + 299 +> [!TIP] 300 +> Рекуррентная формула переводится в код почти дословно: начальные условия → `if (...) return ...;`, формула → `return ...;` с вызовом той же функции. 301 + 302 +--- 303 + 304 +## 10. Шаблоны рекурсивных функций 305 + 306 +### Числа Фибоначчи 307 + 308 +```cpp 309 +int fibonacci(int n) { 310 + if (n == 0) return 0; // база 311 + if (n == 1) return 1; // база 312 + return fibonacci(n - 1) + fibonacci(n - 2); 313 +} 314 +``` 315 + 316 +> [!WARNING] 317 +> Такая функция работает медленно: `fibonacci(5)` вызывает `fibonacci(3)` дважды, `fibonacci(2)` — трижды, и с ростом `n` число вызовов растёт экспоненциально. Для `n ≤ 30` это нормально, для `n = 50` — уже слишком долго. На олимпиадах в таких случаях считают циклом. 318 + 319 +### Сумма цифр числа 320 + 321 +```cpp 322 +int sumDigits(int n) { 323 + if (n < 10) return n; // одна цифра 324 + return n % 10 + sumDigits(n / 10); // последняя цифра + сумма остальных 325 +} 326 +``` 327 + 328 +### НОД (алгоритм Евклида) 329 + 330 +```cpp 331 +int gcd(int a, int b) { 332 + if (b == 0) return a; 333 + return gcd(b, a % b); 334 +} 335 + 336 +// НОД трёх чисел 337 +cout << gcd(gcd(a, b), c) << endl; 338 +``` 339 + 340 +### Переворот числа 341 + 342 +```cpp 343 +int reverseNumber(int n, int reversed = 0) { 344 + if (n == 0) return reversed; 345 + return reverseNumber(n / 10, reversed * 10 + n % 10); 346 +} 347 +``` 348 + 349 +### Проверка на простоту (функция с `bool`) 350 + 351 +```cpp 352 +bool isPrime(int n) { 353 + if (n < 2) return false; 354 + for (int i = 2; i * i <= n; i++) { 355 + if (n % i == 0) return false; 356 + } 357 + return true; 358 +} 359 +``` 360 + 361 +> [!NOTE] 362 +> Условие `i * i <= n` равносильно `i <= sqrt(n)`, но работает только с целыми числами и не требует `<cmath>`. 363 + 364 +--- 365 + 366 +## 11. Чтение данных с разделителями 367 + 368 +Если данные записаны как `15:01:40`, удобно считывать разделитель в переменную типа `char`: 369 + 370 +```cpp 371 +int h, m, s; 372 +char sep; 373 +cin >> h >> sep >> m >> sep >> s; // sep получает ':' 374 +``` 375 + 376 +`cin` прочитает число, затем один символ, затем снова число. 377 + 378 +--- 379 + 380 +## 12. Типичные ошибки 381 + 382 +| ❌ Неправильно | ✅ Правильно | 383 +|---|---| 384 +| Написали функцию, но не вызвали её в `main` | вызвать функцию и использовать результат | 385 +| `int f(int x) { if (x > 0) return 1; }` — нет `return` для `x <= 0` | `return` в каждой ветке | 386 +| `int average(int a, int b)` возвращает дробный результат | тип возвращаемого значения `double` | 387 +| Вызов функции выше её определения без прототипа | прототип сверху или определение до `main` | 388 +| Использование переменной из `main` внутри функции | передать её параметром | 389 +| Рекурсия без базы или с базой, которая не достигается | `if (...) return ...;` в начале функции | 390 +| `gcd(a % b, b)` | `gcd(b, a % b)` | 391 +| `return &a;` — указатель на локальную переменную | возвращать указатель на внешние данные или `nullptr` | 392 +| Одна огромная функция на всё решение | несколько небольших функций | 393 + 394 +--- 395 + 396 +## 13. Шаблон программы 397 + 398 +```cpp 399 +#include <iostream> 400 +using namespace std; 401 + 402 +// вспомогательные функции 403 + 404 +int main() { 405 + // ввод данных 406 + 407 + // вызов функций 408 + 409 + // вывод ответа 410 + 411 + return 0; 412 +} 413 +``` -
Показать разницуСкрыть разницу
1 +# ⭐ Необязательные задачи 2 + 3 +> Дополнительная тренировка по теме. Решайте, если обязательные задачи уже сданы. 4 + 5 +--- 6 + 7 +## Задача 1. Зеркальное слово 8 + 9 +### Условие 10 + 11 +Дано слово. Определите, является ли оно палиндромом, то есть одинаково ли читается слева направо и справа налево. 12 + 13 +### Входные данные 14 + 15 +Одно слово из строчных латинских букв. 16 + 17 +### Выходные данные 18 + 19 +`YES`, если слово — палиндром, иначе `NO`. 20 + 21 +### Примеры 22 + 23 +| Ввод | Вывод | 24 +|---|---| 25 +| `level` | `YES` | 26 +| `hello` | `NO` | 27 +| `a` | `YES` | 28 + 29 +> [!TIP] 30 +> Заведите два индекса: `left = 0` и `right = s.size() - 1`. Пока `left < right`, сравнивайте `s[left]` и `s[right]` и сдвигайте индексы навстречу друг другу. Проверку удобно вынести в функцию `bool isPalindrome(string s)`. 31 + 32 +--- 33 + 34 +## Задача 2. Самая популярная задача 35 + 36 +### Условие 37 + 38 +На олимпиаде было 10 задач с номерами от 1 до 10. Известно, какую задачу сдал каждый из `N` участников. Определите номер задачи, которую сдавали чаще всего. Если таких задач несколько, выведите задачу с наименьшим номером. 39 + 40 +### Входные данные 41 + 42 +В первой строке вводится число `N` (`1 ≤ N ≤ 1000`). Во второй строке — `N` чисел от 1 до 10. 43 + 44 +### Выходные данные 45 + 46 +Номер самой популярной задачи. 47 + 48 +### Примеры 49 + 50 +| Ввод | Вывод | 51 +|---|---| 52 +| `7`<br>`3 1 3 7 1 3 2` | `3` | 53 +| `4`<br>`5 2 5 2` | `2` | 54 + 55 +> [!NOTE] 56 +> Заведите массив счётчиков `int cnt[11] = {0};` и для каждого числа `x` делайте `cnt[x]++`. Индекс 0 не используется — так номера задач совпадают с индексами. 57 + 58 +--- 59 + 60 +## Задача 3. Хороший массив 61 + 62 +### Условие 63 + 64 +Массив называется **хорошим**, если каждое число `x`, которое в нём встречается, встречается ровно `x` раз. Например, массив `3 1 3 2 3 2` хороший: единица встречается один раз, двойка — два, тройка — три. 65 + 66 +Определите, является ли данный массив хорошим. 67 + 68 +### Входные данные 69 + 70 +В первой строке вводится число `N` (`1 ≤ N ≤ 1000`). Во второй строке — `N` чисел от 1 до 1000. 71 + 72 +### Выходные данные 73 + 74 +`YES`, если массив хороший, иначе `NO`. 75 + 76 +### Примеры 77 + 78 +| Ввод | Вывод | 79 +|---|---| 80 +| `6`<br>`3 1 3 2 3 2` | `YES` | 81 +| `3`<br>`2 2 2` | `NO` | 82 +| `1`<br>`1` | `YES` | 83 + 84 +> [!TIP] 85 +> Сначала посчитайте, сколько раз встречается каждое число: `cnt[a[i]]++`. Потом пройдите по массиву ещё раз и проверьте `cnt[a[i]] == a[i]` для каждого элемента. Обратите внимание: здесь нужны два прохода, поэтому числа придётся сохранить в массив. 86 + 87 +--- 88 + 89 +## Задача 4. Все вхождения подстроки 90 + 91 +### Условие 92 + 93 +Даны строка `s` и образец `t`. Выведите позиции (считая с нуля) всех вхождений `t` в `s`. Вхождения могут перекрываться. 94 + 95 +### Входные данные 96 + 97 +Две строки из строчных латинских букв, каждая на отдельной строке. 98 + 99 +### Выходные данные 100 + 101 +Позиции всех вхождений через пробел в порядке возрастания. Если вхождений нет, выведите `-1`. 102 + 103 +### Примеры 104 + 105 +| Ввод | Вывод | 106 +|---|---| 107 +| `abababa`<br>`aba` | `0 2 4` | 108 +| `hello`<br>`ll` | `2` | 109 +| `hello`<br>`xyz` | `-1` | 110 + 111 +> [!TIP] 112 +> Перебирайте начальную позицию `i` от 0, пока `i + t.size() <= s.size()`, и сравнивайте `s.substr(i, t.size()) == t`. Другой способ — `s.find(t, from)`: второй аргумент задаёт, с какой позиции искать. 113 + 114 +--- 115 + 116 +## Задача 5. Минимальное окно 117 + 118 +### Условие 119 + 120 +Дана строка из строчных латинских букв. Найдите длину самой короткой подстроки, которая содержит **все различные буквы**, встречающиеся в строке. 121 + 122 +### Входные данные 123 + 124 +Одна строка длиной до 10⁵ символов. 125 + 126 +### Выходные данные 127 + 128 +Одно число — длина минимального окна. 129 + 130 +### Примеры 131 + 132 +| Ввод | Вывод | 133 +|---|---| 134 +| `aabcbcdbca` | `4` | 135 +| `aaaa` | `1` | 136 +| `abc` | `3` | 137 + 138 +> [!NOTE] 139 +> В первой строке четыре различные буквы `a`, `b`, `c`, `d`. Самое короткое окно, которое их все содержит, — `dbca`, его длина 4. 140 + 141 +> [!TIP] 142 +> Задача на **метод двух указателей**. Двигайте правую границу `r` и считайте буквы в окне массивом `int cnt[26]`. Как только в окне есть все нужные буквы, сдвигайте левую границу `l`, пока это условие не нарушится, и обновляйте ответ. Каждая граница проходит строку один раз, поэтому решение работает быстро даже для 10⁵ символов. -
Показать разницуСкрыть разницу
1 +# ✅ Обязательные задачи (LMS) 2 + 3 +> Эти задачи нужно решить и сдать в LMS. 4 + 5 +--- 6 + 7 +## Задача 1. Рассадка участников 8 + 9 +### Условие 10 + 11 +На олимпиаду по информатике пришло `N` участников. Известно, в какой школе учится каждый из них. В компьютерном классе `N` компьютеров стоят в одну линию вдоль стены. 12 + 13 +Определите, можно ли рассадить участников так, чтобы никакие два участника из одной школы не сидели рядом. 14 + 15 +### Входные данные 16 + 17 +В первой строке вводится число `N` (`1 ≤ N ≤ 1000`). Во второй строке — `N` натуральных чисел, номера школ участников. 18 + 19 +### Выходные данные 20 + 21 +`YES`, если рассадить можно, иначе `NO`. 22 + 23 +### Примеры 24 + 25 +**Ввод:** 26 + 27 +```text 28 +5 29 +1 1 1 2 3 30 +``` 31 + 32 +**Вывод:** 33 + 34 +```text 35 +YES 36 +``` 37 + 38 +**Ввод:** 39 + 40 +```text 41 +4 42 +1 1 1 2 43 +``` 44 + 45 +**Вывод:** 46 + 47 +```text 48 +NO 49 +``` 50 + 51 +> [!NOTE] 52 +> В первом примере подойдёт рассадка `1 2 1 3 1`. Во втором примере троих участников из школы 1 на четыре места без соседства не посадить. 53 + 54 +> [!TIP] 55 +> Посчитайте, сколько участников из самой «многочисленной» школы. Рассадить можно, если это количество не больше `(N + 1) / 2`. 56 + 57 +--- 58 + 59 +## Задача 2. Туристический маршрут 60 + 61 +### Условие 62 + 63 +Дана карта гор Авалона: высоты точек при движении с запада на восток. Туристический маршрут — это отрезок подряд идущих точек, на котором есть подъём и спуск: внутри маршрута найдётся точка, которая **строго выше** и начальной, и конечной точки. 64 + 65 +Найдите длину самого короткого туристического маршрута. Длина маршрута — количество точек в нём. 66 + 67 +### Входные данные 68 + 69 +В первой строке вводится число `N` — количество точек. Во второй строке — `N` целых чисел, высоты точек. 70 + 71 +Гарантируется, что хотя бы один туристический маршрут существует. 72 + 73 +### Выходные данные 74 + 75 +Одно число — минимальная длина маршрута. 76 + 77 +### Пример 78 + 79 +**Ввод:** 80 + 81 +```text 82 +4 83 +15 20 20 10 84 +``` 85 + 86 +**Вывод:** 87 + 88 +```text 89 +4 90 +``` 91 + 92 +> [!NOTE] 93 +> Отрезки `15 20 20` и `20 20 10` не подходят: высота 20 не строго больше конца 20. Подходит только весь маршрут `15 20 20 10` — его длина 4. 94 + 95 +> [!TIP] 96 +> Переберите все пары концов `i < j`, найдите максимум на отрезке `[i, j]` и проверьте, что он строго больше `a[i]` и `a[j]`. Начальное значение ответа возьмите заведомо большим, например `N + 1`. 97 + 98 +--- 99 + 100 +## Задача 3. Близость геномов 101 + 102 +### Условие 103 + 104 +Геном — строка из заглавных латинских букв. **Пары соседних оснований** — это все подстроки длины 2. Степень близости первого генома второму — количество пар первого генома, которые встречаются во втором геноме. 105 + 106 +Если одна и та же пара встречается в первом геноме несколько раз, каждое вхождение считается отдельно. 107 + 108 +### Входные данные 109 + 110 +Две строки `s1` и `s2`, каждая на отдельной строке. 111 + 112 +### Выходные данные 113 + 114 +Одно число — степень близости первого генома второму. 115 + 116 +### Пример 117 + 118 +**Ввод:** 119 + 120 +```text 121 +ABBACAB 122 +BCABB 123 +``` 124 + 125 +**Вывод:** 126 + 127 +```text 128 +4 129 +``` 130 + 131 +> [!NOTE] 132 +> Пары первого генома: `AB`, `BB`, `BA`, `AC`, `CA`, `AB`. Пары второго: `BC`, `CA`, `AB`, `BB`. Во втором геноме встречаются `AB`, `BB`, `CA` и ещё раз `AB` — всего 4. 133 + 134 +> [!TIP] 135 +> Пару выделяйте через `s1.substr(i, 2)`, а наличие во второй строке проверяйте через `s2.find(pair) != string::npos`. 136 + 137 +--- 138 + 139 +## Задача 4. Срез строки 140 + 141 +### Условие 142 + 143 +Дана строка `S` и три целых числа `start`, `end`, `step`. Постройте **срез** строки: символы с индексами `start`, `start + step`, `start + 2·step`, ..., строго меньшими `end`. Индексы считаются с нуля. 144 + 145 +Если `end` больше длины строки, срез идёт до конца строки. 146 + 147 +### Входные данные 148 + 149 +В первой строке — строка `S` без пробелов. Во второй строке — три целых числа `start`, `end`, `step` (`0 ≤ start`, `0 ≤ end`, `step ≥ 1`). 150 + 151 +### Выходные данные 152 + 153 +Получившаяся строка. Если в срез не попал ни один символ, выведите пустую строку. 154 + 155 +### Пример 156 + 157 +**Ввод:** 158 + 159 +```text 160 +abcdefg 161 +1 6 2 162 +``` 163 + 164 +**Вывод:** 165 + 166 +```text 167 +bdf 168 +``` 169 + 170 +> [!NOTE] 171 +> Берутся символы с индексами 1, 3, 5: `b`, `d`, `f`. Следующий индекс 7 уже не меньше 6, поэтому срез заканчивается. Так же работает срез `S[1:6:2]` в Python. 172 + 173 +--- 174 + 175 +## Задача 5. Свободные купе 176 + 177 +### Условие 178 + 179 +В плацкартном вагоне 54 места, пронумерованных от 1 до 54. Вагон разбит на 9 купе. 180 + 181 +Первые 36 мест расположены по левую сторону от прохода: места 1–4 находятся в первом купе, места 5–8 — во втором, и так далее, места 33–36 — в девятом. По правую сторону от прохода — боковые места с номерами от 37 до 54, они нумеруются в противоположном направлении: места 37 и 38 стоят напротив девятого купе, а места 53 и 54 — напротив первого. 182 + 183 +Группа школьников едет на олимпиаду и хочет купить несколько подряд идущих купе целиком, вместе с прилегающими боковыми местами. Даны номера свободных мест. Определите, какое наибольшее число подряд идущих купе полностью свободно. 184 + 185 +### Входные данные 186 + 187 +В первой строке — число `N` (`0 ≤ N ≤ 54`), количество свободных мест. Далее `N` строк — номера свободных мест. 188 + 189 +### Выходные данные 190 + 191 +Одно число — наибольшее количество подряд идущих полностью свободных купе. 192 + 193 +### Пример 194 + 195 +**Ввод:** 196 + 197 +```text 198 +12 199 +5 200 +6 201 +3 202 +4 203 +8 204 +7 205 +51 206 +9 207 +10 208 +54 209 +1 210 +52 211 +``` 212 + 213 +**Вывод:** 214 + 215 +```text 216 +1 217 +``` 218 + 219 +> [!NOTE] 220 +> Полностью свободно только второе купе: места 5, 6, 7, 8 и боковые 51, 52. В первом купе занято место 2, в третьем — места 11 и 12. 221 + 222 +> [!TIP] 223 +> Отметьте свободные места в массиве флажков `bool freeSeat[55] = {false};`. Купе `i` (от 1 до 9) — это места `4*i - 3`, `4*i - 2`, `4*i - 1`, `4*i` и боковые `55 - 2*i`, `56 - 2*i`. Затем найдите самую длинную серию свободных купе. 224 + 225 +--- 226 + 227 +## Задача 6. Операции со строками 228 + 229 +### Условие 230 + 231 +Даны три строки. Над каждой строкой разрешается выполнять две операции: 232 + 233 +- заменить любой символ на два таких же символа (`a` → `aa`); 234 +- заменить два подряд идущих одинаковых символа на один (`aa` → `a`). 235 + 236 +С помощью этих операций нужно сделать все три строки равными одной и той же строке `S`, причём `S` должна быть как можно короче. Если сделать строки равными невозможно, выведите `IMPOSSIBLE`. 237 + 238 +### Входные данные 239 + 240 +Три строки из строчных латинских букв, каждая на отдельной строке. 241 + 242 +### Выходные данные 243 + 244 +Строка `S` или слово `IMPOSSIBLE`. 245 + 246 +### Примеры 247 + 248 +**Ввод:** 249 + 250 +```text 251 +aab 252 +abb 253 +ab 254 +``` 255 + 256 +**Вывод:** 257 + 258 +```text 259 +ab 260 +``` 261 + 262 +**Ввод:** 263 + 264 +```text 265 +ab 266 +ba 267 +ab 268 +``` 269 + 270 +**Вывод:** 271 + 272 +```text 273 +IMPOSSIBLE 274 +``` 275 + 276 +> [!TIP] 277 +> Операции меняют только длину «блоков» одинаковых символов, но не их порядок. Сожмите каждую строку, оставив по одному символу из каждого блока (`aabbbcc` → `abc`). Если все три сжатые строки равны — это и есть ответ. Сжатие удобно оформить функцией `string compress(string s)`. -
Показать разницуСкрыть разницу
1 +# 🧑🏫 Практика 2 + 3 +> Задачи, которые решаем на занятиях. 4 + 5 +--- 6 + 7 +## Задача 1. Количество гласных букв 8 + 9 +### Условие 10 + 11 +Дана строка, состоящая из латинских букв (строчных и заглавных) и пробелов. Определите, сколько в ней гласных букв. 12 + 13 +Гласными считаются буквы `A`, `E`, `I`, `O`, `U`, `Y` и соответствующие строчные `a`, `e`, `i`, `o`, `u`, `y`. 14 + 15 +### Входные данные 16 + 17 +Одна строка. 18 + 19 +### Выходные данные 20 + 21 +Одно число — количество гласных букв. 22 + 23 +### Примеры 24 + 25 +**Ввод:** 26 + 27 +```text 28 +Hello World 29 +``` 30 + 31 +**Вывод:** 32 + 33 +```text 34 +3 35 +``` 36 + 37 +**Ввод:** 38 + 39 +```text 40 +Programming 41 +``` 42 + 43 +**Вывод:** 44 + 45 +```text 46 +3 47 +``` 48 + 49 +> [!NOTE] 50 +> В строке есть пробел, поэтому читать её нужно через `getline(cin, text)`: `cin >> text` прочитает только `Hello`. Дальше перебираем символы `text[i]` и сравниваем каждый с гласными через `||`. 51 + 52 +--- 53 + 54 +## Задача 2. Самая длинная серия 55 + 56 +### Условие 57 + 58 +Дан массив целых чисел. Найдите длину самой длинной последовательности одинаковых подряд идущих элементов. 59 + 60 +### Входные данные 61 + 62 +В первой строке вводится число `N` (`1 ≤ N ≤ 1000`). Во второй строке — `N` целых чисел. 63 + 64 +### Выходные данные 65 + 66 +Одно число — длина самой длинной серии. 67 + 68 +### Пример 69 + 70 +**Ввод:** 71 + 72 +```text 73 +10 74 +4 4 4 2 2 7 7 7 7 1 75 +``` 76 + 77 +**Вывод:** 78 + 79 +```text 80 +4 81 +``` 82 + 83 +> [!NOTE] 84 +> Серии: `4 4 4` (длина 3), `2 2` (2), `7 7 7 7` (4), `1` (1). Самая длинная — 4. Сравнивайте `a[i]` с `a[i - 1]`, поэтому цикл начинается с `i = 1`, а начальные `current` и `max_len` равны 1: даже один элемент — это серия. 85 + 86 +--- 87 + 88 +## Задача 3. Лишняя буква 89 + 90 +### Условие 91 + 92 +Даны две строки. Определите, сколько **различных** букв встречается в первой строке, но отсутствует во второй. 93 + 94 +### Входные данные 95 + 96 +Две строки из строчных латинских букв, каждая на отдельной строке. 97 + 98 +### Выходные данные 99 + 100 +Одно число. 101 + 102 +### Пример 103 + 104 +**Ввод:** 105 + 106 +```text 107 +abracadabra 108 +bar 109 +``` 110 + 111 +**Вывод:** 112 + 113 +```text 114 +2 115 +``` 116 + 117 +> [!NOTE] 118 +> В первой строке есть буквы `a`, `b`, `r`, `c`, `d`, во второй — только `a`, `b`, `r`. Отсутствуют `c` и `d`. Чтобы не посчитать одну букву дважды, отмечайте уже учтённые буквы в массиве `bool used[26] = {false};` по индексу `c - 'a'`. 119 + 120 +--- 121 + 122 +## Задача 4. Самый частый символ 123 + 124 +### Условие 125 + 126 +Дана строка из строчных латинских букв. Определите символ, который встречается в ней чаще всего. Если таких символов несколько, выведите тот, который раньше стоит в алфавите. 127 + 128 +### Входные данные 129 + 130 +Одна строка. 131 + 132 +### Выходные данные 133 + 134 +Один символ. 135 + 136 +### Пример 137 + 138 +**Ввод:** 139 + 140 +```text 141 +mississippi 142 +``` 143 + 144 +**Вывод:** 145 + 146 +```text 147 +i 148 +``` 149 + 150 +> [!NOTE] 151 +> Буквы `i` и `s` встречаются по 4 раза, но `i` раньше в алфавите. Подсчитайте частоты в массиве `int cnt[26] = {0};` и найдите индекс максимума. Сравнение строго `>`, а не `>=`: тогда при равенстве останется буква с меньшим индексом. Вывод: `char('a' + maxIdx)`. 152 + 153 +--- 154 + 155 +## Задача 5. Локальные максимумы 156 + 157 +### Условие 158 + 159 +Дан массив целых чисел. Элемент называется **локальным максимумом**, если он строго больше обоих своих соседей. Первый и последний элементы локальными максимумами не считаются. 160 + 161 +Выведите количество локальных максимумов. 162 + 163 +### Входные данные 164 + 165 +В первой строке вводится число `N` (`1 ≤ N ≤ 1000`). Во второй строке — `N` целых чисел. 166 + 167 +### Выходные данные 168 + 169 +Одно число — количество локальных максимумов. 170 + 171 +### Пример 172 + 173 +**Ввод:** 174 + 175 +```text 176 +7 177 +1 5 2 4 3 6 1 178 +``` 179 + 180 +**Вывод:** 181 + 182 +```text 183 +3 184 +``` 185 + 186 +> [!NOTE] 187 +> Локальные максимумы — 5, 4 и 6. Цикл идёт от `i = 1` до `i < n - 1`, иначе обращение к `a[i - 1]` или `a[i + 1]` выйдет за границы массива. Если `N < 3`, цикл не выполнится ни разу и ответ равен 0. -
Показать разницуСкрыть разницу
1 +# 📘 Тема 1.6. Необходимый материал 2 + 3 +> Шпаргалка по массивам и строкам. 4 + 5 +--- 6 + 7 +## 1. Зачем нужны массивы 8 + 9 +До сих пор числа обрабатывались «на лету»: считали — сразу использовали — забыли. Но иногда к элементу нужно вернуться: пройти по данным второй раз, сравнить соседей, отсортировать. Для этого данные нужно **хранить**. 10 + 11 +**Массив** — набор элементов **одного типа**, которые лежат в памяти подряд и доступны по номеру. 12 + 13 +**Итерируемые объекты** — структуры, элементы которых можно перебирать в цикле: 14 + 15 +| Структура | Пример | Особенность | 16 +|---|---|---| 17 +| массив | `int arr[5] = {1, 2, 3, 4, 5};` | фиксированный размер | 18 +| строка | `string s = "Hello";` | последовательность символов | 19 +| вектор | `vector<int> v = {10, 20, 30};` | размер может меняться | 20 + 21 +Термины: 22 + 23 +| Термин | Значение | 24 +|---|---| 25 +| **элемент** | одно значение в массиве | 26 +| **индекс** | номер элемента, начинается с `0` | 27 +| **размер** | количество элементов | 28 +| **обход** | перебор всех элементов циклом | 29 + 30 +--- 31 + 32 +## 2. Объявление и инициализация 33 + 34 +```cpp 35 +тип имя[размер]; 36 +``` 37 + 38 +```cpp 39 +int a[100]; // 100 целых чисел, значения не заданы 40 +int b[5] = {10, 20, 30, 40, 50}; 41 +int c[5] = {0}; // все элементы равны 0 42 +int d[] = {3, 1, 4}; // размер 3 вычисляется сам 43 +bool used[26] = {false}; // 26 флажков, все false 44 +``` 45 + 46 +> [!IMPORTANT] 47 +> Размер массива должен быть константой. В олимпиадных задачах массив обычно объявляют с запасом по максимальному ограничению: если `N ≤ 1000`, пишут `int a[1000];` (или чуть больше), а реально используют первые `n` элементов. 48 + 49 +> [!WARNING] 50 +> Локальный массив без инициализации (`int a[100];` внутри `main`) содержит «мусор». Если массив используется как счётчики, обязательно пишите `= {0}`. 51 + 52 +--- 53 + 54 +## 3. Индексация 55 + 56 +- Первый элемент — `a[0]`. 57 +- Последний элемент массива из `n` элементов — `a[n - 1]`. 58 + 59 +```cpp 60 +int arr[5] = {10, 20, 30, 40, 50}; 61 +cout << arr[0] << endl; // 10 62 +cout << arr[4] << endl; // 50 63 +arr[2] = 99; // теперь {10, 20, 99, 40, 50} 64 +``` 65 + 66 +| Индекс | 0 | 1 | 2 | 3 | 4 | 67 +|---|---|---|---|---|---| 68 +| Значение | 10 | 20 | 30 | 40 | 50 | 69 + 70 +Почему с нуля? Индекс — это **смещение** от начала массива. Первый элемент лежит в самом начале, его смещение равно 0. 71 + 72 +> [!CAUTION] 73 +> `arr[5]` для массива из пяти элементов — **выход за границы**. Компилятор это не проверяет: программа может выдать мусор, испортить соседние переменные или упасть. 74 + 75 +--- 76 + 77 +## 4. Заполнение и вывод 78 + 79 +### С клавиатуры 80 + 81 +```cpp 82 +int n; 83 +cin >> n; 84 +int a[1000]; 85 +for (int i = 0; i < n; i++) { 86 + cin >> a[i]; 87 +} 88 +``` 89 + 90 +### По формуле 91 + 92 +```cpp 93 +int a[10]; 94 +for (int i = 0; i < 10; i++) { 95 + a[i] = i + 1; // 1, 2, ..., 10 96 +} 97 +``` 98 + 99 +### Случайными числами 100 + 101 +```cpp 102 +#include <cstdlib> // rand, srand 103 +#include <ctime> // time 104 + 105 +srand(time(0)); // без этого rand() каждый раз даёт одно и то же 106 +for (int i = 0; i < 10; i++) { 107 + a[i] = rand() % 100 + 1; // от 1 до 100 108 +} 109 +``` 110 + 111 +> [!NOTE] 112 +> Случайные числа удобны, чтобы проверить свою программу на большом тесте. В решениях олимпиадных задач они не нужны. 113 + 114 +### Вывод 115 + 116 +```cpp 117 +for (int i = 0; i < n; i++) { 118 + cout << a[i] << " "; 119 +} 120 +cout << endl; 121 +``` 122 + 123 +--- 124 + 125 +## 5. Шаблоны обработки массива 126 + 127 +### Минимум и максимум 128 + 129 +```cpp 130 +int mn = a[0]; 131 +int mx = a[0]; 132 +for (int i = 1; i < n; i++) { 133 + if (a[i] < mn) mn = a[i]; 134 + if (a[i] > mx) mx = a[i]; 135 +} 136 +``` 137 + 138 +> [!IMPORTANT] 139 +> Начинайте с первого элемента массива, а не с нуля. Для массива `{-5, -10, -3}` начальное `mx = 0` даст ответ 0, хотя максимум равен −3. 140 + 141 +### Среднее значение 142 + 143 +```cpp 144 +int sum = 0; 145 +for (int i = 0; i < n; i++) { 146 + sum += a[i]; 147 +} 148 +double average = (double)sum / n; 149 +``` 150 + 151 +Без `(double)` получится целочисленное деление: `61 / 6 = 10`, а не `10.1667`. 152 + 153 +### Сравнение с соседями 154 + 155 +```cpp 156 +// локальные максимумы: первый и последний элементы не проверяем 157 +int cnt = 0; 158 +for (int i = 1; i < n - 1; i++) { 159 + if (a[i] > a[i - 1] && a[i] > a[i + 1]) { 160 + cnt++; 161 + } 162 +} 163 +``` 164 + 165 +### Самая длинная серия одинаковых 166 + 167 +```cpp 168 +int best = 1, current = 1; 169 +for (int i = 1; i < n; i++) { 170 + if (a[i] == a[i - 1]) current++; 171 + else current = 1; 172 + if (current > best) best = current; 173 +} 174 +``` 175 + 176 +> [!WARNING] 177 +> При обращении к `a[i - 1]` цикл начинается с `i = 1`, при обращении к `a[i + 1]` — заканчивается на `i < n - 1`. Иначе будет выход за границы. 178 + 179 +--- 180 + 181 +## 6. Массив через указатель 182 + 183 +Имя массива — это адрес его первого элемента. Поэтому `a[i]` и `*(a + i)` — одно и то же. 184 + 185 +```cpp 186 +int arr[5] = {10, 20, 30, 40, 50}; 187 +int* ptr = arr; // указатель на arr[0] 188 + 189 +cout << *ptr << endl; // 10 190 +cout << *(ptr + 3) << endl; // 40 191 +``` 192 + 193 +Размер массива, объявленного в той же функции, можно узнать через `sizeof`: 194 + 195 +```cpp 196 +int arr[] = {10, 5, 8, 20, 3, 15}; 197 +int size = sizeof(arr) / sizeof(arr[0]); // 6 198 +``` 199 + 200 +--- 201 + 202 +## 7. Символы и ASCII 203 + 204 +Каждому символу соответствует числовой код по таблице **ASCII**. 205 + 206 +| Символ | Код | 207 +|---|---| 208 +| `' '` (пробел) | 32 | 209 +| `'0'` ... `'9'` | 48 ... 57 | 210 +| `'A'` ... `'Z'` | 65 ... 90 | 211 +| `'a'` ... `'z'` | 97 ... 122 | 212 + 213 +Цифры и буквы идут подряд, поэтому с символами можно работать как с числами: 214 + 215 +```cpp 216 +char ch = 'A'; 217 +cout << (int)ch << endl; // 65 218 +cout << (char)97 << endl; // a 219 + 220 +int d = '7' - '0'; // 7 — символ-цифра в число 221 +char c = '0' + 5; // '5' — число в символ-цифру 222 +int idx = 'c' - 'a'; // 2 — номер буквы в алфавите 223 +char letter = 'a' + 3; // 'd' 224 +``` 225 + 226 +### Подсчёт букв массивом 227 + 228 +```cpp 229 +int cnt[26] = {0}; 230 +for (int i = 0; i < s.size(); i++) { 231 + cnt[s[i] - 'a']++; // для строчных латинских букв 232 +} 233 +``` 234 + 235 +> [!TIP] 236 +> Приём `c - 'a'` превращает букву в индекс от 0 до 25. Так массив `cnt[26]` становится «таблицей частот» букв, а `bool used[26]` — набором отметок «буква уже встречалась». 237 + 238 +--- 239 + 240 +## 8. Строки `string` 241 + 242 +`string` — строка из символов. Нужен `#include <string>`. 243 + 244 +```cpp 245 +string s; 246 +cin >> s; // одно слово (до пробела) 247 +getline(cin, s); // вся строка, вместе с пробелами 248 +``` 249 + 250 +| Операция | Что делает | Пример для `s = "Hello"` | 251 +|---|---|---| 252 +| `s.size()`, `s.length()` | длина строки | `5` | 253 +| `s[i]` | символ с индексом `i` | `s[1]` → `'e'` | 254 +| `s.substr(start, len)` | подстрока длины `len` с позиции `start` | `s.substr(1, 3)` → `"ell"` | 255 +| `s.find(t)` | позиция первого вхождения `t` | `s.find("l")` → `2` | 256 +| `s.push_back(c)`, `s += c` | добавить символ в конец | `"Hello!"` | 257 +| `s.append(t)`, `s += t` | добавить строку в конец | `"Hello World"` | 258 +| `s.back()` | последний символ | `'o'` | 259 +| `s.empty()` | пустая ли строка | `false` | 260 +| `s == t`, `s < t` | сравнение строк | `s == "Hello"` → `true` | 261 +| `s.compare(t)` | `0`, если равны; `<0` или `>0` иначе | `s.compare("Hello")` → `0` | 262 + 263 +### Перебор символов 264 + 265 +```cpp 266 +for (int i = 0; i < s.size(); i++) { 267 + cout << s[i] << " "; 268 +} 269 +``` 270 + 271 +### Поиск подстроки 272 + 273 +```cpp 274 +if (s.find("ab") != string::npos) { 275 + cout << "found" << endl; 276 +} 277 +``` 278 + 279 +> [!IMPORTANT] 280 +> Если подстрока не найдена, `find` возвращает специальное значение `string::npos`. Сравнивайте именно с ним, а не с `-1`. 281 + 282 +> [!WARNING] 283 +> - `substr(start, len)` — второй аргумент это **длина**, а не конечная позиция. 284 +> - `cin >> s` читает только до пробела. Для строки с пробелами нужен `getline(cin, s)`. 285 +> - Если перед `getline` был `cin >> n`, в потоке остался перевод строки. Пропустите его: `cin.ignore();`. 286 +> - `s.size()` возвращает беззнаковое число. Для пустой строки `s.size() - 1` — это не −1, а огромное число. Пишите `i + 1 < s.size()` или приводите к `int`. 287 + 288 +--- 289 + 290 +## 9. Строки `char[]` 291 + 292 +Строка в стиле языка C — массив символов, который заканчивается **нулевым символом** `'\0'`. Функции для неё лежат в `<cstring>`. 293 + 294 +```cpp 295 +char s[100] = "Hello"; // H e l l o \0 296 +``` 297 + 298 +| Функция | Что делает | 299 +|---|---| 300 +| `strlen(s)` | длина строки | 301 +| `strcpy(dest, src)` | копирует `src` в `dest` | 302 +| `strcat(dest, src)` | дописывает `src` в конец `dest` | 303 +| `strcmp(s1, s2)` | сравнивает строки: `0`, если равны | 304 +| `strstr(s1, s2)` | ищет `s2` внутри `s1` | 305 + 306 +> [!NOTE] 307 +> `string` удобнее и безопаснее: он сам знает свою длину и сам выделяет память. `char[]` нужно знать, чтобы читать чужой код, но в своих решениях используйте `string`. 308 + 309 +--- 310 + 311 +## 10. Пример: палиндром 312 + 313 +Два индекса идут навстречу друг другу с краёв строки и сравнивают символы. 314 + 315 +```cpp 316 +bool isPalindrome(const string& s) { 317 + int left = 0; 318 + int right = s.size() - 1; 319 + while (left < right) { 320 + if (s[left] != s[right]) { 321 + return false; 322 + } 323 + left++; 324 + right--; 325 + } 326 + return true; 327 +} 328 +``` 329 + 330 +`const string& s` — передача по ссылке: строка не копируется, а `const` запрещает её менять. 331 + 332 +--- 333 + 334 +## 11. Пример: пары соседних символов 335 + 336 +Задача «Инопланетный геном»: сколько пар соседних букв первой строки встречаются во второй строке. 337 + 338 +```cpp 339 +string s1, s2; 340 +cin >> s1 >> s2; 341 + 342 +int ans = 0; 343 +for (int i = 0; i + 1 < s1.size(); i++) { 344 + string pair = s1.substr(i, 2); 345 + if (s2.find(pair) != string::npos) { 346 + ans++; 347 + } 348 +} 349 +cout << ans << endl; 350 +``` 351 + 352 +Для `s1 = "ABBACAB"` и `s2 = "BCABB"`: пары первой строки `AB, BB, BA, AC, CA, AB`, из них во второй встречаются `AB, BB, CA, AB` — ответ 4. 353 + 354 +--- 355 + 356 +## 12. Типичные ошибки 357 + 358 +| ❌ Неправильно | ✅ Правильно | 359 +|---|---| 360 +| `a[n]` — последний элемент | `a[n - 1]` | 361 +| `for (int i = 0; i <= n; i++)` для обхода массива | `for (int i = 0; i < n; i++)` | 362 +| `int cnt[26];` и сразу `cnt[x]++` | `int cnt[26] = {0};` | 363 +| `int mx = 0;` для максимума массива | `int mx = a[0];` | 364 +| `a[i] > a[i - 1]` при `i = 0` | начинать цикл с `i = 1` | 365 +| `sum / n` для среднего | `(double)sum / n` | 366 +| `cin >> s` для строки с пробелами | `getline(cin, s)` | 367 +| `s.find(t) == -1` | `s.find(t) == string::npos` | 368 +| `s.substr(2, 5)` как «с 2 по 5» | `s.substr(start, len)` — второй аргумент длина | 369 +| `s[i]` вместо `s[i] - '0'` для цифры | `s[i] - '0'` | 370 +| `if (s = "YES")` | `if (s == "YES")` | 371 + 372 +--- 373 + 374 +## 13. Шаблон программы 375 + 376 +```cpp 377 +#include <iostream> 378 +#include <string> 379 +using namespace std; 380 + 381 +int main() { 382 + int n; 383 + cin >> n; 384 + 385 + int a[1000]; 386 + for (int i = 0; i < n; i++) { 387 + cin >> a[i]; 388 + } 389 + 390 + // обработка массива 391 + 392 + // вывод ответа 393 + 394 + return 0; 395 +} 396 +```