add task_002
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
#include <hack/logger/logger.hpp>
|
||||
#include "tasks/001.hpp"
|
||||
#include "tasks/002.hpp"
|
||||
|
||||
auto main() -> int
|
||||
{
|
||||
alg::tasks::run_001();
|
||||
alg::tasks::run_002();
|
||||
}
|
||||
|
||||
|
||||
79
src/tasks/002.hpp
Normal file
79
src/tasks/002.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <hack/logger/logger.hpp>
|
||||
|
||||
namespace alg::tasks
|
||||
{
|
||||
// Рассмотрим последовательность целых чисел длины n.
|
||||
// По ней двигается «окно» длины k: сначала в «окне» находятся
|
||||
// первые k чисел, на следующем шаге в «окне» уже будут находиться
|
||||
// k чисел, начиная со второго, и так далее до конца последовательности.
|
||||
// Требуется для каждого положения «окна» определить минимум в нём.
|
||||
/* пример:
|
||||
7 3
|
||||
1 3 2 4 5 3 1
|
||||
|
||||
Рассмотрим все доступные положения скользящего окна:
|
||||
|1 3 2| 4 5 3 1 - min(1, 3, 2) = 1
|
||||
1 |3 2 4| 5 3 1 - min(3, 2, 4) = 2
|
||||
1 3 |2 4 5| 3 1 - min(2, 4, 5) = 2
|
||||
1 3 2 |4 5 3| 1 - min(4, 5, 3) = 3
|
||||
1 3 2 4 |5 3 1| - min(5, 3, 1) = 1
|
||||
|
||||
результат [1, 2, 2, 3, 1]
|
||||
*/
|
||||
inline void run_002()
|
||||
{
|
||||
// Ускоряем ввод/вывод для больших объёмов данных
|
||||
std::ios_base::sync_with_stdio(false);
|
||||
std::cin.tie(nullptr);
|
||||
|
||||
int n, k;
|
||||
std::cin >> n >> k;
|
||||
std::vector<int> a(n);
|
||||
for (int i = 0; i < n; ++i) std::cin >> a[i];
|
||||
|
||||
// deque хранит индексы элементов текущих окон.
|
||||
// Значения a[dq[0]], a[dq[1]], ... всегда идут в неубывающем порядке.
|
||||
std::deque<int> dq;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
// 1. Удаляем индекс из начала, если он вышел за пределы окна
|
||||
if (!dq.empty() && dq.front() == i - k) dq.pop_front();
|
||||
|
||||
// 2. Удаляем индексы с конца, если их значения >= текущего элемента.
|
||||
// Они больше никогда не станут минимумом окна.
|
||||
while (!dq.empty() && a[dq.back()] >= a[i]) dq.pop_back();
|
||||
|
||||
// 3. Добавляем текущий индекс
|
||||
dq.push_back(i);
|
||||
|
||||
// 4. Если окно полностью сформировалось (прошли минимум k элементов),
|
||||
// минимум всегда находится в начале deque.
|
||||
if (i >= k - 1) std::cout << a[dq.front()] << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Основное:
|
||||
1. **`dq.front()`** всегда содержит индекс минимального элемента в текущем окне.
|
||||
2. При сдвиге окна мы удаляем устаревший индекс слева (`i - k`).
|
||||
3. Перед добавлением нового элемента мы "очищаем" хвост очереди от элементов, которые больше или равны новому.
|
||||
Они бесполезны, так как новый элемент `a[i]` моложе (живёт дольше в окне) и не больше их, поэтому минимумом они стать не смогут.
|
||||
4. Каждый элемент добавляется в `deque` ровно один раз и удаляется тоже не более одного
|
||||
раза → **амортизированное время `O(1)` на шаг**, итого `O(n)` на всю последовательность.
|
||||
|
||||
Сложность:
|
||||
- **Время:** `O(n)` (проход по массиву, каждое число добавляется/удаляется из `deque` максимум 1 раз)
|
||||
- **Память:** `O(n)` для хранения массива + `O(k)` для `deque` (в худшем случае)
|
||||
|
||||
Примечание по вводу/выводу:
|
||||
В коде использованы `ios_base::sync_with_stdio(false); cin.tie(nullptr);` и `'\n'` вместо `endl`.
|
||||
Это критически важно для задач с `n > 10^5`, иначе программа может не уложиться в лимит времени из-за медленного I/O в C++.
|
||||
*/
|
||||
@@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
// находит все простые числа от 0 до n
|
||||
// Решето Эратосфена
|
||||
namespace alg
|
||||
{
|
||||
inline std::vector<int> find_primes(int n)
|
||||
{
|
||||
std::vector<int> r;
|
||||
std::vector<bool> is_prime(n + 1, true);
|
||||
|
||||
// 0 и 1 не являются простыми числами
|
||||
is_prime[0] = is_prime[1] = false;
|
||||
|
||||
// Исключаем все чётные числа, кроме 2
|
||||
for (int i = 4; i <= n; i += 2) is_prime[i] = false;
|
||||
|
||||
// Перебираем только нечётные числа, начиная с 3
|
||||
for (int p = 3; p * p <= n; p += 2)
|
||||
if (is_prime[p])
|
||||
for (int i = p * p; i <= n; i += 2 * p) is_prime[i] = false;
|
||||
|
||||
for (int p = 3; p <= n; p += 2)
|
||||
if (is_prime[p]) r.push_back(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Называется алгоритм Евклида
|
||||
// Находит наибольший общий делитель
|
||||
// см. Алгоритмический тренинг стр. 80
|
||||
namespace alg
|
||||
{
|
||||
inline int gcd(int a, int b)
|
||||
{
|
||||
while (b != 0)
|
||||
{
|
||||
int r = a%b;
|
||||
a = b;
|
||||
b = r;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
#include "gcd.hpp"
|
||||
|
||||
// Малая теорема Ферма: Если p - число простое и a - целое число, не делящееся на p, то при
|
||||
// возведении a в степень p - 1, наибольший общий делитель между результатом и p будет равен 1
|
||||
// Но там есть ньюансы, называются обманщиками Ферма. По этому тесты нужно проводить на нескольких числах a.
|
||||
// По этому если тестов много (у нас по уолчанию 1000) то вероятность того что появятся обманщики Ферам 1/2^1000
|
||||
// Проверяет число на простоту
|
||||
namespace alg
|
||||
{
|
||||
inline bool is_prime(int a, int max_test = 1000)
|
||||
{
|
||||
// начинаем с 4 т.к. 2 и 3 простые числа
|
||||
for (int i = 4; i < max_test; ++i)
|
||||
if (gcd(std::pow(i, a - 1), a) == 1) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
// Находит все простые множители заданного числа.
|
||||
// Простое число - это число > 1, которое делится на 1 и на само себя.
|
||||
namespace alg
|
||||
{
|
||||
inline std::vector<int> prime_factors_v1(int a)
|
||||
{
|
||||
std::vector<int> result;
|
||||
int i = 2;
|
||||
|
||||
while (i < a)
|
||||
{
|
||||
while (a%i == 0)
|
||||
{
|
||||
result.push_back(i);
|
||||
a /= i;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if (a > 1) result.push_back(a);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::vector<int> prime_factors_v2(int a)
|
||||
{
|
||||
std::vector<int> result;
|
||||
|
||||
while(a%2 == 0)
|
||||
{
|
||||
result.push_back(2);
|
||||
a = a/2;
|
||||
}
|
||||
|
||||
int i = 3;
|
||||
int max_faxtor = std::sqrt(a);
|
||||
|
||||
while (i <= max_faxtor)
|
||||
{
|
||||
while (a%i == 0)
|
||||
{
|
||||
result.push_back(i);
|
||||
a /= i;
|
||||
max_faxtor = std::sqrt(a);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
|
||||
if (a > 1) result.push_back(a);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user