From 30a20abaa7a52ed7f52a7beeb4122273c54e4b43 Mon Sep 17 00:00:00 2001 From: Ashpool Date: Tue, 17 Sep 2024 14:46:28 +0200 Subject: [PATCH] partial: 20c dijkstra --- README.md | 4 +- problems/20c-dijkstra/.clangd | 2 + problems/20c-dijkstra/.unsolved | 0 problems/20c-dijkstra/.vscode/launch.json | 13 + problems/20c-dijkstra/Makefile | 20 + problems/20c-dijkstra/README.md | 3 + problems/20c-dijkstra/main.cpp | 487 ++++++++++++++++++++++ templates/main.cpp.j2 | 9 +- 8 files changed, 534 insertions(+), 4 deletions(-) create mode 100644 problems/20c-dijkstra/.clangd create mode 100644 problems/20c-dijkstra/.unsolved create mode 100644 problems/20c-dijkstra/.vscode/launch.json create mode 100644 problems/20c-dijkstra/Makefile create mode 100644 problems/20c-dijkstra/README.md create mode 100644 problems/20c-dijkstra/main.cpp diff --git a/README.md b/README.md index d4dfadb..37d1b82 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,9 @@ Author's Codeforces profile: [Ashpool](https://codeforces.com/profile/Ashpool) ## Statistics * Total problems in Codeforces problem set: 9790 - * Problems started: 228 + * Problems started: 229 * Problems solved: 227 - * Problems not solved: 1 + * Problems not solved: 2 Percentage of solved problems: 2.3187% diff --git a/problems/20c-dijkstra/.clangd b/problems/20c-dijkstra/.clangd new file mode 100644 index 0000000..315a8a4 --- /dev/null +++ b/problems/20c-dijkstra/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: [-std=c++23, -Wall, -Wextra] diff --git a/problems/20c-dijkstra/.unsolved b/problems/20c-dijkstra/.unsolved new file mode 100644 index 0000000..e69de29 diff --git a/problems/20c-dijkstra/.vscode/launch.json b/problems/20c-dijkstra/.vscode/launch.json new file mode 100644 index 0000000..97f7935 --- /dev/null +++ b/problems/20c-dijkstra/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug Dijkstra?", + "program": "${workspaceFolder}/20c-dijkstra.out", + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} diff --git a/problems/20c-dijkstra/Makefile b/problems/20c-dijkstra/Makefile new file mode 100644 index 0000000..a273abb --- /dev/null +++ b/problems/20c-dijkstra/Makefile @@ -0,0 +1,20 @@ +.POSIX: +CXX = clang++ +CXXFLAGS = -g -std=c++23 -Wall -Wextra + +SRCS = main.cpp +OBJS = $(SRCS:.cpp=.o) + +EXE = 20c-dijkstra.out + +all: $(EXE) + +$(EXE): $(OBJS) + $(CXX) $(CXXFLAGS) -o $@ $^ + +clean: + rm -f $(EXE) $(OBJS) +run: $(EXE) + ./$(EXE) + +.PHONY: all clean run diff --git a/problems/20c-dijkstra/README.md b/problems/20c-dijkstra/README.md new file mode 100644 index 0000000..ffed93e --- /dev/null +++ b/problems/20c-dijkstra/README.md @@ -0,0 +1,3 @@ +# 20C. Dijkstra? + +[Problem statement on Codeforces](https://codeforces.com/problemset/problem/20/C?locale=en). \ No newline at end of file diff --git a/problems/20c-dijkstra/main.cpp b/problems/20c-dijkstra/main.cpp new file mode 100644 index 0000000..1db87f0 --- /dev/null +++ b/problems/20c-dijkstra/main.cpp @@ -0,0 +1,487 @@ +// Problem: 20C. Dijkstra? +// Problem statement: https://codeforces.com/problemset/problem/20/C?locale=en +// Solution author: Artem Zhurikhin (https://codeforces.com/profile/Ashpool) +// Solution license: the Unlicense (Public Domain) +// More solutions: https://github.com/ashpool37/codeforces-cxx20 + +/* #region templates */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ll = long long; +using ull = unsigned long long; + +unsigned digit_to_unsigned(char c) { + char cs[2] = "\0"; + cs[0] = c; + unsigned result; + std::from_chars(cs, cs+1, result); + return result; +} + +char unsigned_to_digit(unsigned n) { + char cs[2] = "\0"; + std::to_chars(cs, cs+1, n); + return cs[0]; +} + +template +auto counted(T start, std::size_t count, T step = 1) { + using namespace std::ranges::views; + return iota(0u, count) | transform([=](std::size_t i){ return start + step * static_cast(i); }); +} + +auto counted(std::size_t count) { + return counted(0ull, count, 1ull); +} + +template +auto power(BaseT base, ExponentT exponent) { + std::common_type_t result = 1; + while(exponent--) result *= base; + return result; +} + +template +concept ReadableFromStream = requires(std::istream is, T value) { + { is >> value }; +}; + +class FromIstreamHelper { + std::istream& input_stream; + std::optional count; + + FromIstreamHelper(FromIstreamHelper const&) = delete; + FromIstreamHelper(FromIstreamHelper&&) = delete; + FromIstreamHelper& operator=(FromIstreamHelper const&) = delete; + FromIstreamHelper& operator=(FromIstreamHelper&&) = delete; + + FromIstreamHelper(std::istream& input_stream, std::size_t count) + : input_stream(input_stream), count(count) + {} + FromIstreamHelper(std::istream& input_stream) + : input_stream(input_stream), count(std::nullopt) + {} + + friend auto from_istream(std::istream& input_stream); + friend auto from_istream(std::istream& input_stream, std::size_t count); + +public: + template + operator T() && { + T result; + input_stream >> result; + return result; + } + + template + operator std::vector() && { + std::vector result; + if(count) { + result.reserve(*count); + std::copy_n(std::istream_iterator(input_stream), *count, std::back_inserter(result)); + } + return result; + } + + template + operator std::list() && { + std::list result; + if(count) { + std::copy_n(std::istream_iterator(input_stream), *count, std::back_inserter(result)); + } + return result; + } + + template + operator std::forward_list() && { + std::forward_list result; + auto result_it = result.before_begin(); + auto input_it = std::istream_iterator(input_stream); + if(count) { + result_it = result.insert_after(result_it, *input_it); + for(auto const _ : counted(*count - 1uz)) { + input_it++; + result_it = result.insert_after(result_it, *input_it); + } + } + return result; + } + + template + operator std::array() && { + std::array result; + std::copy_n(std::istream_iterator(input_stream), array_size, std::begin(result)); + return result; + } +}; + +auto from_istream(std::istream& input_stream) { + return FromIstreamHelper(input_stream); +} + +auto from_istream(std::istream& input_stream, std::size_t count) { + return FromIstreamHelper(input_stream, count); +} + +auto from_cin() { + return from_istream(std::cin); +} + +auto from_cin(std::size_t count) { + return from_istream(std::cin, count); +} + +std::string line_from_istream(std::istream& input_stream) { + std::string result; + std::getline(input_stream, result); + return result; +} + +std::string line_from_cin() { + return line_from_istream(std::cin); +} + +void istream_skip_line(std::istream& input_stream) { + input_stream.ignore(std::numeric_limits::max(), '\n'); +} + +void cin_skip_line() { + istream_skip_line(std::cin); +} + +bool space_p(char ch) +{ + return static_cast(std::isspace(static_cast(ch))); +} + +void istream_skip_ws(std::istream& input_stream) { + while(space_p(input_stream.peek())) input_stream.get(); +} + +void cin_skip_ws() { + istream_skip_ws(std::cin); +} + +template OutputIt> +void generate_multiples(T limit, T base, OutputIt it) { + T multiple; + for(T i = 2; (multiple = base * i) <= limit; i++) { + *it = multiple; + it++; + } +} + +template +std::vector multiples_to_vector(T limit, T base, bool include_base = true) { + std::vector result; + if(include_base) result.push_back(base); + generate_multiples(limit, base, std::back_inserter(result)); + return result; +} + +template OutputIt> +void generate_primes(T limit, OutputIt it) { + std::unordered_set composite; + for(T i = 2; i <= limit; i++) { + if(not composite.contains(i)) { + *it = i; + it++; + if(i * i <= limit) generate_multiples(limit, i, std::inserter(composite, composite.begin())); + } + } +} + +template +std::vector primes_to_vector(T limit) { + std::vector result; + generate_primes(limit, std::back_inserter(result)); + return result; +} + +template +concept Summable = requires(T lhs, T rhs) { + { lhs + rhs }; +}; + +template +requires Summable> +auto sum(R const& range) { + return std::ranges::fold_left(range, 0, std::plus()); +} + +template +T ceil_div(T lhs, T rhs) { + if(rhs == 0u) throw std::runtime_error("Division by zero"); + return (lhs + rhs - 1u) / rhs; +} + +template +concept WriteableToStream = requires(std::ostream os, T value) { + { os << value }; +}; + +template +class OptionalPrintHelper { + std::optional const& optional; + D const& default_value; + OptionalPrintHelper(std::optional const& optional, D const& default_value) + : optional(optional), default_value(default_value) + {} + + template + friend std::ostream& operator<<(std::ostream& output_stream, OptionalPrintHelper const& opt_print_helper); + template + friend OptionalPrintHelper print_opt(std::optional const& optional, D_ const& default_value); +}; + +template +std::ostream& operator<<(std::ostream& output_stream, OptionalPrintHelper const& opt_print_helper) { + if(opt_print_helper.optional.has_value()) + output_stream << *(opt_print_helper.optional); + else output_stream << opt_print_helper.default_value; + return output_stream; +} + +template +OptionalPrintHelper print_opt(std::optional const& optional, D const& default_value) { + return OptionalPrintHelper(optional, default_value); +} + +/* #endregion */ + +#include +#include +#include +#include +#include + +template +class iint { + struct infinity_tag {}; + + iint(infinity_tag, signed char inf_sign) : value(0), infinity_sign(inf_sign) {} + + I value = 0; + signed char infinity_sign = 0; +public: + iint() = default; + iint(I val) : value(val), infinity_sign(0) {} + + static iint positive_infinity() { return iint(infinity_tag{}, +1); } + static iint negative_infinity() { return iint(infinity_tag{}, -1); } + + auto operator<=>(iint const& other) const { + if (infinity_sign == 0 && other.infinity_sign == 0) { + return value <=> other.value; + } else if (infinity_sign == 0) { + return (-other.infinity_sign) <=> 0; + } else if (other.infinity_sign == 0) { + return infinity_sign <=> 0; + } else { + return infinity_sign <=> other.infinity_sign; + } + } + + template + auto operator<=>(J const& rhs) const { + if (infinity_sign == 0) { + return value <=> rhs; + } else if (infinity_sign > 0) { + return std::strong_ordering::greater; + } else { + return std::strong_ordering::less; + } + } + + template + friend auto operator<=>(J const& lhs, iint const& rhs) { + if (rhs.infinity_sign == 0) { + return lhs <=> rhs.value; + } else if (rhs.infinity_sign > 0) { + return std::strong_ordering::less; + } else { + return std::strong_ordering::greater; + } + } + + template + explicit operator T() const { + if (infinity_sign == 0) { + return static_cast(value); + } else if (infinity_sign > 0) { + return std::numeric_limits::max(); + } else { + if constexpr (std::is_signed_v) { + return std::numeric_limits::min(); + } else { + throw std::overflow_error("Cannot cast negative infinity to unsigned integral type"); + } + } + } + + explicit operator bool() const { + if (infinity_sign != 0) { + return true; + } else { + return value != 0; + } + } +}; + +struct GraphInputEdge { + std::pair nodes; + unsigned weight; + + GraphInputEdge sub1() const { + return GraphInputEdge{.nodes = {nodes.first - 1uz, nodes.second - 1uz}, .weight = weight}; + } +}; + +std::istream& operator>>(std::istream& input_stream, GraphInputEdge& edge) { + input_stream >> edge.nodes.first >> edge.nodes.second >> edge.weight; + return input_stream; +} + +class Graph { + struct Edge { + std::size_t to; + unsigned weight; + }; + + std::vector> nodes; +public: + template + Graph(std::size_t const node_count, std::size_t const edge_count, EdgesR&& edges) + : nodes(node_count) + { + auto edge_it = std::ranges::begin(edges); + auto const get_edge = [this, &edge_it]() { + auto const& edge = *edge_it; + nodes[edge.nodes.first].push_front(Edge{edge.nodes.second, edge.weight}); + nodes[edge.nodes.second].push_front(Edge{edge.nodes.first, edge.weight}); + }; + if(edge_count > 0uz) { + get_edge(); + for(auto const _ : counted(edge_count - 1uz)) { + edge_it++; + get_edge(); + } + } + } + + [[nodiscard]] std::size_t size() const { + return nodes.size(); + } + + void remove_single_edge_loops() { + for(std::size_t node_id = 0uz; node_id < size(); node_id++) { + nodes[node_id] = + std::views::filter(nodes[node_id], [node_id](Edge const& edge) { return edge.to != node_id; }) + | std::ranges::to>(); + } + } + + void remove_parallel_edges() { + for(std::size_t node_id = 0uz; node_id < size(); node_id++) { + std::map min_edges; + for(Edge const& edge : nodes[node_id]) { + auto const dest_it = min_edges.find(edge.to); + if(dest_it == min_edges.end()) + min_edges[edge.to] = edge.weight; + else + min_edges[edge.to] = std::min(edge.weight, dest_it->second); + }; + nodes[node_id] = min_edges + | std::views::transform([](auto const& pair) { + return Edge{.to = pair.first, .weight = pair.second}; + }) + | std::ranges::to>(); + } + } + + [[nodiscard]] std::unordered_map dijkstra_shortest_paths(std::size_t source_id) const { + std::unordered_map previous; + std::vector> distances(size(), iint::positive_infinity()); + struct DijkstraNode { + std::size_t node_id; + unsigned distance_seen; + + auto operator<=>(DijkstraNode const& rhs) const { + return distance_seen <=> rhs.distance_seen; + } + }; + std::priority_queue, std::greater> to_visit; + + previous[source_id] = source_id; + distances[source_id] = 0u; + to_visit.push({.node_id = source_id, .distance_seen = 0u}); + + while(not to_visit.empty()) { + DijkstraNode const here = to_visit.top(); + to_visit.pop(); + + if(distances[here.node_id] >= here.distance_seen) { + for(auto const edge : nodes[here.node_id]) { + unsigned neighbour_distance_seen = here.distance_seen + edge.weight; + if(distances[edge.to] > neighbour_distance_seen) { + distances[edge.to] = neighbour_distance_seen; + previous[edge.to] = here.node_id; + to_visit.push({.node_id = edge.to, .distance_seen = neighbour_distance_seen}); + } + } + } + } + + return previous; + } + + [[nodiscard]] static std::optional> + dijkstra_reconstruct_path(std::unordered_map const& previous, std::size_t destination) { + auto prev_it = previous.find(destination); + if(prev_it == previous.end()) return std::nullopt; + + std::forward_list result; + result.push_front(destination); + while(prev_it->second != prev_it->first) { + result.push_front(prev_it->second); + prev_it = previous.find(prev_it->second); + } + return result; + } +}; + +int main() { + std::size_t const node_count = from_cin(); + std::size_t const edge_count = from_cin(); + Graph const graph = [node_count, edge_count]() { + Graph result(node_count, edge_count, + std::views::istream(std::cin) + | std::views::transform(&GraphInputEdge::sub1)); + result.remove_parallel_edges(); + result.remove_single_edge_loops(); + return result; + }(); + std::unordered_map dijkstra_previous = + graph.dijkstra_shortest_paths(0uz); + std::optional> shortest_path = + Graph::dijkstra_reconstruct_path(dijkstra_previous, node_count - 1uz); + if(not shortest_path.has_value()) + std::cout << -1 << std::endl; + else { + for(auto const node_id : *shortest_path) + std::cout << node_id + 1uz << " "; + std::cout << std::endl; + } +} diff --git a/templates/main.cpp.j2 b/templates/main.cpp.j2 index 291c479..611d105 100644 --- a/templates/main.cpp.j2 +++ b/templates/main.cpp.j2 @@ -107,10 +107,15 @@ public: template operator std::forward_list() && { std::forward_list result; + auto result_it = result.before_begin(); + auto input_it = std::istream_iterator(input_stream); if(count) { - std::copy_n(std::istream_iterator(input_stream), *count, std::front_inserter(result)); + result_it = result.insert_after(result_it, *input_it); + for(auto const _ : counted(*count - 1uz)) { + input_it++; + result_it = result.insert_after(result_it, *input_it); + } } - result.reverse(); return result; }