План v2 по диагностике (квиз 14/32) + 4 новые задачи (hash, heap, dijkstra, subnet) + тренажёр 60 карточек
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
// Задача 12 — кратчайший путь (Дейкстра).
|
||||
// Реализуй функцию ниже. Заглушка намеренно не проходит проверку.
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
long long shortest_path(int n,
|
||||
const std::vector<std::tuple<int, int, int>>& edges,
|
||||
int src, int dst) {
|
||||
(void)n;
|
||||
(void)edges;
|
||||
(void)src;
|
||||
(void)dst;
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Задача 12 — Дейкстра на priority_queue (C++)
|
||||
|
||||
```c++
|
||||
long long shortest_path(int n,
|
||||
const std::vector<std::tuple<int,int,int>>& edges, // (u, v, w)
|
||||
int src, int dst);
|
||||
```
|
||||
|
||||
Требования:
|
||||
- рёбра **ориентированные**, вес `w >= 0`, self-loop допустим; вершины `0..n-1`;
|
||||
- вернуть длину кратчайшего пути `src -> dst` (`long long`, веса суммируются до 10^9);
|
||||
- недостижимость → `-1`; `src == dst` → `0`;
|
||||
- вес рёбер 0 допустим (условие применимости Дейкстры — **неотрицательные** веса);
|
||||
- 100 000 вершин и 200 000 рёбер: быстрее 2 секунд. Значит нужна `std::priority_queue`
|
||||
(O((V+E) log V)), а не O(V²) перебор минимума.
|
||||
|
||||
Подсказки по разбору (после сдачи): «ленивое» удаление устаревших записей из очереди
|
||||
(`if (d != dist[v]) continue;`), почему нельзя Дейкстру с отрицательными рёбрами и когда
|
||||
нужен Беллман–Форд.
|
||||
|
||||
Проверка: `python3 grade.py 12`. Критерий: все `ok`, сборка без предупреждений.
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include "solution.cpp"
|
||||
|
||||
static int failures = 0;
|
||||
#define CHECK(cond, name) do { if (cond) std::printf("ok %s\n", name); \
|
||||
else { std::printf("FAIL %s (line %d)\n", name, __LINE__); ++failures; } } while (0)
|
||||
|
||||
using E = std::tuple<int, int, int>;
|
||||
|
||||
int main() {
|
||||
{
|
||||
std::vector<E> e = {{0, 1, 4}, {0, 2, 1}, {2, 1, 1}, {1, 3, 1}};
|
||||
CHECK(shortest_path(4, e, 0, 3) == 3, "0->3 через 2: 1+1+1 = 3");
|
||||
CHECK(shortest_path(4, e, 0, 0) == 0, "src == dst -> 0");
|
||||
CHECK(shortest_path(4, e, 3, 0) == -1, "обратного пути нет -> -1");
|
||||
CHECK(shortest_path(4, e, 0, 2) == 1, "прямой путь 0->2 = 1");
|
||||
}
|
||||
{
|
||||
std::vector<E> e = {{0, 1, 5}, {0, 2, 1}, {2, 1, 1}};
|
||||
CHECK(shortest_path(3, e, 0, 1) == 2, "длинное ребро не должно побеждать короткий путь");
|
||||
std::vector<E> z = {{0, 1, 0}, {1, 2, 0}};
|
||||
CHECK(shortest_path(3, z, 0, 2) == 0, "нулевые веса допустимы");
|
||||
std::vector<E> self = {{0, 0, 7}, {0, 1, 2}};
|
||||
CHECK(shortest_path(2, self, 0, 1) == 2, "self-loop не ломает поиск");
|
||||
std::vector<E> par = {{0, 1, 3}, {0, 1, 7}};
|
||||
CHECK(shortest_path(2, par, 0, 1) == 3, "параллельные рёбра: берём минимальное");
|
||||
std::vector<E> none;
|
||||
CHECK(shortest_path(2, none, 0, 1) == -1, "без рёбер -> -1");
|
||||
}
|
||||
{
|
||||
// цепочка 0-1-2-...-(n-1) по 1000, плюс «ловушки»: обратные рёбра по 100000.
|
||||
// Ответ детерминирован: идти по цепочке дешевле, чем прыгать назад и снова вперёд.
|
||||
const int n = 100000;
|
||||
std::vector<E> e;
|
||||
e.reserve(200000);
|
||||
for (int i = 0; i + 1 < n; ++i) e.emplace_back(i, i + 1, 1000);
|
||||
for (int i = 1; i < n; ++i) e.emplace_back(i, i - 1, 100000);
|
||||
e.emplace_back(0, 0, 99999999);
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
long long d = shortest_path(n, e, 0, n - 1);
|
||||
double sec = std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
|
||||
std::printf(" (100k вершин, 200k рёбер: dist=%lld, time=%.3fs)\n", d, sec);
|
||||
CHECK(d == (long long)(n - 1) * 1000, "цепочка: кратчайший путь по цепочке");
|
||||
CHECK(sec < 2.0, "100k вершин за < 2 c");
|
||||
}
|
||||
{
|
||||
// большие веса: сумма должна влезать в long long
|
||||
std::vector<E> e;
|
||||
for (int i = 0; i + 1 < 2000; ++i) e.emplace_back(i, i + 1, 1000000);
|
||||
CHECK(shortest_path(2000, e, 0, 1999) == 1999LL * 1000000LL, "большие веса: 1999 * 1e6");
|
||||
}
|
||||
std::printf(failures ? "\nFAILURES: %d\n" : "\nALL PASS\n", failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user