diff --git a/Cargo.toml b/Cargo.toml index ef526b1..d669917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "contest-algorithms" +name = "contest-llamas" version = "0.3.1-alpha.0" -authors = ["Aram Ebtekar"] +authors = ["Luis Galup"] edition = "2021" description = "Common algorithms and data structures for programming contests" -repository = "https://github.com/EbTech/rust-algorithms" +repository = "https://github.com/legalup/rust-llama-cookbook" readme = "README.md" keywords = ["competitive", "programming", "codeforces"] categories = ["algorithms", "data-structures"] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 9b434df..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Aram Ebtekar - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..8abb436 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) for portions of project Foo are held by Aram Ebtekar 2017 as part of project rust-algorithms. All other copyright for project rust-ars-algorithmica are held by Luis Galup 2023. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index cae59f9..040d420 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,15 @@ -# Contest Algorithms in Rust +# Rust /\r5 /\lgorithmic/\ -[![Crates.io Version](https://img.shields.io/crates/v/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) -[![Documentation](https://docs.rs/contest-algorithms/badge.svg)](https://docs.rs/contest-algorithms) -[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bevyengine/bevy/blob/master/LICENSE) -[![Crates.io Downloads](https://img.shields.io/crates/d/contest-algorithms.svg)](https://crates.io/crates/contest-algorithms) -[![Build Status](https://travis-ci.org/EbTech/rust-algorithms.svg?branch=master)](https://travis-ci.org/EbTech/rust-algorithms) -[![Gitter](https://badges.gitter.im/rust-algos/community.svg)](https://gitter.im/rust-algos/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +This is a collection of classic data structures and interesting algorithms, emphasizing clarity, elegance and understanding over generality and speed. One design criterion is transparency: explain the names and concepts, annotate with complexity, be obvious in intention. Another is simplicity: the Rust ecosystem is full of well meaning crates, and I have intentionally decided to stick with data structures and algorithms only found in std for universality. -A collection of classic data structures and algorithms, emphasizing usability, beauty and clarity over full generality. As such, this should be viewed not as a blackbox *library*, but as a whitebox *cookbook* demonstrating the design and implementation of algorithms. I hope it will be useful to students and educators, as well as fans of algorithmic programming contests. +This is a fork of EbTech's amazing repo. My intention is to change the fundamental graph base classes to be more in keeping with our intuition and understanding of graphs, do more data representation encapsulation, decouple algorithm implementation from knowledge of the internals of their datastructures, put in more tests of algorithms validity and speed,and when happy with that, start adding far more algorithms. -This repository is distributed under the [MIT License](LICENSE). Contest submissions need not include the license text. Enjoy! +My hope is that someday my kids will use this to learn about algorithms, something that I have always been obsessed about. Its also intended for students/teachers of algorithmica. I also think that its useful for competive programming; and that Rust is in many ways a good language for that. Except for the fact that its not easy to make a linked list... -## For Students and Educators +Rust is a language that makes algorithms smaller and simpler, eliminating some deep bugs that are inevitable from the complexity in other languages (like c++). Its functional nature enforces elegance and compactness, its compiler assists in correctness. -When learning a new algorithm or data structure, it's often helpful to see or play with a concrete implementation. As such, this repository catalogues several classic algorithms in their simplest forms. -In addition, the Rust language has outstanding pedagogical attributes. Its compiler acts as a teacher, enforcing strict discipline while pointing to clearer ways to structure one's logic. - -## For Programming Contests - -The original intent of this project was to build a reference for use in programming contests. As a result, it contains algorithms that are frequently useful to have in one's toolkit, with an emphasis on code that is concise and easy to modify under time pressure. - -Most competitive programmers rely on C++ for its fast execution time. However, it's notoriously unsafe, diverting a considerable share of the contestant's time and attention on mistake prevention and debugging. Java is the next most popular choice, offering a little safety at some expense to speed of coding and execution. - -To my delight, I found that Rust eliminates entire classes of bugs, while reducing visual clutter to make the rest easier to spot. And, it's *fast*. There's a learning curve, to be sure. However, a proficient Rust programmer stands to gain a competitive advantage as well as a more pleasant experience! +![llama](https://user-images.githubusercontent.com/9121210/218507152-5a9646d5-c8bb-4937-acfb-8834410975fd.jpg) Some contest sites and online judges that support Rust: - [Codeforces](https://codeforces.com) @@ -34,22 +20,11 @@ Some contest sites and online judges that support Rust: - [HackerRank](https://www.hackerrank.com/contests) - [Timus](http://acm.timus.ru/help.aspx?topic=rust) -The following support pre-2018 versions of Rust: -- [Google Kick Start and Code Jam](https://codingcompetitions.withgoogle.com) - -For help in getting started, you may check out [some of my past submissions](https://codeforces.com/contest/1168/submission/55200038). - -## Programming Language Advocacy - -My other goal is to appeal to developers who feel limited by ancient (yet still mainstream) programming languages, by demonstrating the power of modern techniques. - -Rather than try to persuade you with words, this repository aims to show by example. If you'd like to learn the language, I recommend [the official book](https://doc.rust-lang.org/book/) or [Programming Rust](https://www.amazon.com/Programming-Rust-Fast-Systems-Development-dp-1492052590/dp/1492052590). - # Contents ## [Graphs](src/graph/) -### [Graph representations](src/graph/mod.rs) +### [Graph representations](src/graph/graph.rs) - Integer index-based adjacency list representation - Disjoint set union @@ -60,6 +35,7 @@ Rather than try to persuade you with words, this repository aims to show by exam - Kruskal's minimum spanning tree - Dijkstra's single-source shortest paths - DFS pre-order traversal +- Floyd warshall shortest paths ### [Connected components](src/graph/connectivity.rs) @@ -74,12 +50,12 @@ Rather than try to persuade you with words, this repository aims to show by exam - Dinic's blocking maximum flow - Minimum cut -- Hopcroft-Karp bipartite matching +- Hopcroft-Karp bipartite maximum matching O(\sqrt(V)*E) - Minimum cost maximum flow ## [Math](src/math/) -### [Number theory](src/math/mod.rs) +### [Number theory](src/math/division.rs) - Greatest common divisor - Canonical solution to Bezout's identity diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..13f5bdf --- /dev/null +++ b/flake.lock @@ -0,0 +1,133 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1698882062, + "narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "8c9fa2545007b49a5db5f650ae91f227672c3877", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1700794826, + "narHash": "sha256-RyJTnTNKhO0yqRpDISk03I/4A67/dp96YRxc86YOPgU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5a09cb4b393d58f9ed0d9ca1555016a8543c2ac8", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1698611440, + "narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1681358109, + "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1700965014, + "narHash": "sha256-vprUv4maYeo0zW5uyEznXsv6DXwE+lLk4dcyOz6rVBI=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ee42d1bf90ceed1b1d2e4b79f947f7513f3a3506", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..3904860 --- /dev/null +++ b/flake.nix @@ -0,0 +1,62 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + rust-overlay.url = "github:oxalica/rust-overlay"; + }; + + outputs = inputs: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ "x86_64-linux" ]; + perSystem = { config, self', pkgs, lib, system, ... }: + let + runtimeDeps = with pkgs; [ alsa-lib speechd ]; + buildDeps = with pkgs; [ pkg-config rustPlatform.bindgenHook ]; + devDeps = with pkgs; [ gdb ]; + + cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); + msrv = cargoToml.package.rust-version; + + rustPackage = features: + (pkgs.makeRustPlatform { + cargo = pkgs.rust-bin.stable.latest.minimal; + rustc = pkgs.rust-bin.stable.latest.minimal; + }).buildRustPackage { + inherit (cargoToml.package) name version; + src = ./.; + cargoLock.lockFile = ./Cargo.lock; + buildFeatures = features; + buildInputs = runtimeDeps; + nativeBuildInputs = buildDeps; + # Uncomment if your cargo tests require networking or otherwise + # don't play nicely with the Nix build sandbox: + # doCheck = false; + }; + + mkDevShell = rustc: + pkgs.mkShell { + shellHook = '' + export RUST_SRC_PATH=${pkgs.rustPlatform.rustLibSrc} + ''; + buildInputs = runtimeDeps; + nativeBuildInputs = buildDeps ++ devDeps ++ [ rustc ]; + }; + in { + _module.args.pkgs = import inputs.nixpkgs { + inherit system; + overlays = [ (import inputs.rust-overlay) ]; + }; + + packages.default = self'.packages.example; + devShells.default = self'.devShells.nightly; + + packages.example = (rustPackage "foobar"); + packages.example-base = (rustPackage ""); + + devShells.nightly = (mkDevShell (pkgs.rust-bin.selectLatestNightlyWith + (toolchain: toolchain.default))); + devShells.stable = (mkDevShell pkgs.rust-bin.stable.latest.default); + devShells.msrv = (mkDevShell pkgs.rust-bin.stable.${msrv}.default); + }; + }; +} diff --git a/src/caching.rs b/src/caching.rs index ed22656..8959d62 100644 --- a/src/caching.rs +++ b/src/caching.rs @@ -28,11 +28,13 @@ where U: std::cmp::Eq + std::hash::Hash + Copy, V: Copy, { + /* /// Constuctor for the Casher /// # Examples /// ``` /// # use contest_algorithms::caching::Cacher; /// let mut squared = Cacher::new(|n: u32| n*n); + */ /// ``` pub fn new(calculation: F) -> Cacher { Cacher { @@ -40,18 +42,19 @@ where values: HashMap::new(), } } - /// Performs a lookup into the HashMap to see if the value has already /// been calculated. If it has, returns the value. If it has not, /// calls the function, stores the value, then returns the value. /// # Examples /// ``` + /* /// # use contest_algorithms::caching::Cacher; /// let mut squared = Cacher::new(|n: u32| n*n); /// /// // This is where we call the function /// let sixteen = squared.call(4); /// ``` + */ // TODO: whenever Rust's Entry API gains the ability to take ownership of // arg only when necessary, this method should follow the same practice. // Also, Cacher should implement Fn(U)->V once this is possible. diff --git a/src/graph.rs b/src/graph.rs new file mode 100644 index 0000000..3cc666d --- /dev/null +++ b/src/graph.rs @@ -0,0 +1,5 @@ +pub mod connectivity; +pub mod disjoint_set; +pub mod flow; +pub mod graph; +pub mod util; diff --git a/src/graph/connectivity.rs b/src/graph/connectivity.rs index aaf854c..b5ac63f 100644 --- a/src/graph/connectivity.rs +++ b/src/graph/connectivity.rs @@ -1,11 +1,12 @@ //! Graph connectivity structures. -use super::Graph; + +use super::graph::{DirectedGraph, UndirectedGraph}; /// Helper struct that carries data needed for the depth-first searches in /// ConnectivityGraph's constructor. struct ConnectivityData { time: usize, - vis: Box<[usize]>, + visited: Box<[usize]>, low: Box<[usize]>, v_stack: Vec, e_stack: Vec, @@ -15,7 +16,7 @@ impl ConnectivityData { fn new(num_v: usize) -> Self { Self { time: 0, - vis: vec![0; num_v].into_boxed_slice(), + visited: vec![0; num_v].into_boxed_slice(), low: vec![0; num_v].into_boxed_slice(), v_stack: vec![], e_stack: vec![], @@ -24,7 +25,7 @@ impl ConnectivityData { fn visit(&mut self, u: usize) { self.time += 1; - self.vis[u] = self.time; + self.visited[u] = self.time; self.low[u] = self.time; self.v_stack.push(u); } @@ -40,47 +41,30 @@ impl ConnectivityData { /// /// - Connected components (CC), /// - Strongly connected components (SCC), -/// - 2-edge-connected components (2ECC), -/// - 2-vertex-connected components (2VCC) /// /// Multiple-edges and self-loops are correctly handled. -pub struct ConnectivityGraph<'a> { +pub struct ConnectivityDirectedGraph<'a> { /// Immutable graph, frozen for the lifetime of the ConnectivityGraph object. - pub graph: &'a Graph, + pub graph: &'a DirectedGraph, /// ID of a vertex's CC, SCC or 2ECC, whichever applies. Range 1 to num_cc. pub cc: Vec, - /// ID of an edge's 2VCC, where applicable. Ranges from 1 to num_vcc. - pub vcc: Vec, /// Total number of CCs, SCCs or 2ECCs, whichever applies. pub num_cc: usize, - /// Total number of 2VCCs, where applicable. - pub num_vcc: usize, } -impl<'a> ConnectivityGraph<'a> { +impl<'a> ConnectivityDirectedGraph<'a> { /// Computes CCs (connected components), SCCs (strongly connected - /// components), 2ECCs (2-edge-connected components), and/or 2VCCs - /// (2-vertex-connected components), depending on the parameter and graph: - /// - is_directed == true on directed graph: SCCs in rev-topological order - /// - is_directed == true on undirected graph: CCs - /// - is_directed == false on undirected graph: 2ECCs and 2VCCs - /// - is_directed == false on directed graph: undefined behavior - pub fn new(graph: &'a Graph, is_directed: bool) -> Self { + /// components),depending on the parameter and graph: + pub fn new(graph: &'a DirectedGraph) -> Self { let mut connect = Self { graph, cc: vec![0; graph.num_v()], - vcc: vec![0; graph.num_e()], num_cc: 0, - num_vcc: 0, }; let mut data = ConnectivityData::new(graph.num_v()); for u in 0..graph.num_v() { - if data.vis[u] == 0 { - if is_directed { - connect.scc(&mut data, u); - } else { - connect.bcc(&mut data, u, graph.num_e() + 1); - } + if data.visited[u] == 0 { + connect.scc(&mut data, u); } } connect @@ -89,14 +73,14 @@ impl<'a> ConnectivityGraph<'a> { fn scc(&mut self, data: &mut ConnectivityData, u: usize) { data.visit(u); for (_, v) in self.graph.adj_list(u) { - if data.vis[v] == 0 { - self.scc(data, v); + if data.visited[*v] == 0 { + self.scc(data, *v); } - if self.cc[v] == 0 { - data.lower(u, data.low[v]); + if self.cc[*v] == 0 { + data.lower(u, data.low[*v]); } } - if data.vis[u] == data.low[u] { + if data.visited[u] == data.low[u] { self.num_cc += 1; while let Some(v) = data.v_stack.pop() { self.cc[v] = self.num_cc; @@ -109,6 +93,7 @@ impl<'a> ConnectivityGraph<'a> { /// From the directed implication graph corresponding to a 2-SAT clause, /// finds a satisfying assignment if it exists or returns None otherwise. + /// only for directed graphs pub fn two_sat_assign(&self) -> Option> { (0..self.graph.num_v() / 2) .map(|i| { @@ -126,40 +111,110 @@ impl<'a> ConnectivityGraph<'a> { /// Gets the vertices of a graph according to a topological order of the /// strongly connected components. Most often used on DAGs. pub fn topological_sort(&self) -> Vec { - let mut vertices = (0..self.graph.num_v()).collect::>(); + let mut vertices = (0..self.graph.num_v()).collect::>(); //;turbofish vertices.sort_unstable_by_key(|&u| self.num_cc - self.cc[u]); vertices } +} + +/// Represents the decomposition of a graph into any of its constituent parts: +/// +/// - Connected components (CC), +/// - 2-edge-connected components (2ECC), +/// - 2-vertex-connected components (2VCC) +/// +/// Multiple-edges and self-loops are correctly handled. +/// this class find connected components, as well as 2 connected components +pub struct ConnectivityUndirectedGraph<'a> { + /// Immutable undiredted graph, frozen for the lifetime of this object. + pub graph: &'a UndirectedGraph, + /// ID of a vertex's CC or 2ECC, whichever applies. Range 1 to num_cc. + pub cc: Vec, + /// ID of an edge's 2VCC, where applicable. Ranges from 1 to num_vcc. + pub vcc: Vec, + /// keeps track of articuallation points, aka cut vertex's + pub is_articulation_point: Vec, + /// Total number of CCs, SCCs or 2ECCs, whichever applies. + pub num_cc: usize, + /// Total number of 2VCCs, where applicable. + pub num_vcc: usize, +} - fn bcc(&mut self, data: &mut ConnectivityData, u: usize, par: usize) { +impl<'a> ConnectivityUndirectedGraph<'a> { + /// Computes CCs (connected components), SCCs (strongly connected + /// components), 2ECCs (2-edge-connected components), and/or 2VCCs + /// (2-vertex-connected components), depending on the parameter and graph: + /// - is_directed == true on directed graph: SCCs in rev-topological order + /// - is_directed == true on undirected graph: CCs + /// - is_directed == false on undirected graph: 2ECCs and 2VCCs + /// - is_directed == false on directed graph: undefined behavior + pub fn new(graph: &'a UndirectedGraph) -> Self { + let mut connect = Self { + graph, + cc: vec![0; graph.num_v()], + vcc: vec![0; graph.num_e()], + is_articulation_point: vec![false; graph.num_v()], + num_cc: 0, + num_vcc: 0, + }; + let mut data = ConnectivityData::new(graph.num_v()); + for u in 0..graph.num_v() { + if data.visited[u] == 0 { + connect.biconnected(&mut data, u, usize::MAX); + } + } + connect + } + + ///Tarjans algorithm for finding cut vertex. this also find biconnected components + /// https://www.geeksforgeeks.org/articulation-points-or-cut-vertices-in-a-graph/ + fn biconnected(&mut self, data: &mut ConnectivityData, u: usize, parent: usize) { data.visit(u); - for (e, v) in self.graph.adj_list(u) { - if data.vis[v] == 0 { + let mut children = 0usize; + for (er, vr) in self.graph.adj_list(u) { + let e = *er; + let v = *vr; + if data.visited[v] == 0 { + children += 1; data.e_stack.push(e); - self.bcc(data, v, e); + self.biconnected(data, v, u); + data.lower(u, data.low[v]); - if data.vis[u] <= data.low[v] { + if parent < usize::MAX && data.visited[u] <= data.low[v] { // u is a cut vertex unless it's a one-child root self.num_vcc += 1; + //data.e_stack.pop(); while let Some(top_e) = data.e_stack.pop() { - self.vcc[top_e] = self.num_vcc; - self.vcc[top_e ^ 1] = self.num_vcc; - if e ^ top_e <= 1 { + if self.vcc[top_e] == 0 { + //never been assigned b4 + self.vcc[top_e] = self.num_vcc; + } + if e == top_e { break; } } + self.is_articulation_point[u] = true; } - } else if data.vis[v] < data.vis[u] && e ^ par != 1 { - data.lower(u, data.vis[v]); + } else if data.visited[v] < data.visited[u] && e != parent { + data.lower(u, data.visited[v]); data.e_stack.push(e); } else if v == u { // e is a self-loop self.num_vcc += 1; self.vcc[e] = self.num_vcc; - self.vcc[e ^ 1] = self.num_vcc; } } - if data.vis[u] == data.low[u] { + // if u is a root of dfs tree and has two or more children + if parent == usize::MAX && children > 1 { + self.num_vcc += 1; + + while let Some(top_e) = data.e_stack.pop() { + self.vcc[top_e] = self.num_vcc; + } + + self.is_articulation_point[u] = true; + } + if data.visited[u] == data.low[u] { // par is a cut edge unless par==-1 self.num_cc += 1; while let Some(v) = data.v_stack.pop() { @@ -171,22 +226,20 @@ impl<'a> ConnectivityGraph<'a> { } } - /// In an undirected graph, determines whether u is an articulation vertex. + /// In an undirected graph, determines whether u is an cut vertex. pub fn is_cut_vertex(&self, u: usize) -> bool { - if let Some(first_e) = self.graph.first[u] { - self.graph - .adj_list(u) - .any(|(e, _)| self.vcc[first_e] != self.vcc[e]) - } else { - false - } + //return self.is_articulation_point[u]; + + let first_e = self.graph.adj_lists[u][0].0; + self.graph + .adj_list(u) + .any(|(e, _)| self.vcc[first_e] != self.vcc[*e]) } /// In an undirected graph, determines whether e is a bridge pub fn is_cut_edge(&self, e: usize) -> bool { - let u = self.graph.endp[e ^ 1]; - let v = self.graph.endp[e]; - self.cc[u] != self.cc[v] + let ev = Vec::from_iter(&self.graph.edges[e]); + self.cc[*ev[0]] != self.cc[*ev[1]] } } @@ -196,7 +249,7 @@ mod test { #[test] fn test_toposort() { - let mut graph = Graph::new(4, 5); + let mut graph = DirectedGraph::new(4, 5); graph.add_edge(0, 0); graph.add_edge(0, 2); graph.add_edge(3, 2); @@ -204,36 +257,39 @@ mod test { graph.add_edge(1, 0); assert_eq!( - ConnectivityGraph::new(&graph, true).topological_sort(), + ConnectivityDirectedGraph::new(&graph).topological_sort(), vec![3, 1, 0, 2] ); } #[test] fn test_two_sat() { - let mut graph = Graph::new(6, 8); + let mut graph = DirectedGraph::new(6, 8); let (x, y, z) = (0, 2, 4); graph.add_two_sat_clause(x, z); graph.add_two_sat_clause(y ^ 1, z ^ 1); graph.add_two_sat_clause(y, y); assert_eq!( - ConnectivityGraph::new(&graph, true).two_sat_assign(), + ConnectivityDirectedGraph::new(&graph).two_sat_assign(), Some(vec![true, true, false]) ); graph.add_two_sat_clause(z, z); - assert_eq!(ConnectivityGraph::new(&graph, true).two_sat_assign(), None); + assert_eq!( + ConnectivityDirectedGraph::new(&graph).two_sat_assign(), + None + ); } #[test] fn test_biconnected() { - let mut graph = Graph::new(3, 6); - graph.add_undirected_edge(0, 1); - graph.add_undirected_edge(1, 2); - graph.add_undirected_edge(1, 2); + let mut graph = UndirectedGraph::new(3, 6); + graph.add_edge(0, 1); + graph.add_edge(1, 2); + graph.add_edge(1, 2); - let cg = ConnectivityGraph::new(&graph, false); + let cg = ConnectivityUndirectedGraph::new(&graph); let bridges = (0..graph.num_e()) .filter(|&e| cg.is_cut_edge(e)) .collect::>(); @@ -241,7 +297,35 @@ mod test { .filter(|&u| cg.is_cut_vertex(u)) .collect::>(); - assert_eq!(bridges, vec![0, 1]); + for idx in 0..graph.num_e() { + if cg.is_cut_edge(idx) { + println!(" edge {} is a cut edge", idx) + } else { + println!(" edge {} is not a cut edge", idx) + } + } + assert_eq!(bridges, vec![0]); + assert_eq!(articulation_points, vec![1]); + } + + #[test] + fn test_articulation_points() { + let mut graph = UndirectedGraph::new(7, 8); + graph.add_edge(0, 1); + graph.add_edge(1, 2); + graph.add_edge(0, 2); + graph.add_edge(1, 3); + graph.add_edge(3, 5); + graph.add_edge(5, 4); + graph.add_edge(4, 1); + graph.add_edge(1, 6); + + let cg = ConnectivityUndirectedGraph::new(&graph); + + let articulation_points = (0..graph.num_v()) + .filter(|&u| cg.is_cut_vertex(u)) + .collect::>(); + assert_eq!(articulation_points, vec![1]); } } diff --git a/src/graph/disjoint_set.rs b/src/graph/disjoint_set.rs new file mode 100644 index 0000000..3004730 --- /dev/null +++ b/src/graph/disjoint_set.rs @@ -0,0 +1,38 @@ +//! Basic graph module without explicit support for deletion. +//! +//! # Panics +//! +//! All methods will panic if given an out-of-bounds element index. + +/// Represents a union of disjoint sets. Each set's elements are arranged in a +/// tree, whose root is the set's representative. +pub struct DisjointSets { + parent: Vec, +} + +impl DisjointSets { + /// Initializes disjoint sets containing one element each. + pub fn new(size: usize) -> Self { + Self { + parent: (0..size).collect(), + } + } + + /// Finds the set's representative. Do path compression along the way to make + /// future queries faster. + pub fn find(&mut self, u: usize) -> usize { + let pu = self.parent[u]; + if pu != u { + self.parent[u] = self.find(pu); + } + self.parent[u] + } + + /// Merges the sets containing u and v into a single set containing their + /// union. Returns true if u and v were previously in different sets. + pub fn merge(&mut self, u: usize, v: usize) -> bool { + let (pu, pv) = (self.find(u), self.find(v)); + self.parent[pu] = pv; + pu != pv + } +} diff --git a/src/graph/flow.rs b/src/graph/flow.rs index 0b6e684..9a0d570 100644 --- a/src/graph/flow.rs +++ b/src/graph/flow.rs @@ -1,10 +1,10 @@ //! Maximum flows, matchings, and minimum cuts. -use super::{AdjListIterator, Graph}; +use super::graph::{AdjListIterator, DirectedGraph}; /// Representation of a network flow problem with (optional) costs. pub struct FlowGraph { /// Owned graph, managed by this FlowGraph object. - pub graph: Graph, + pub graph: DirectedGraph, /// Edge capacities. pub cap: Vec, /// Edge cost per unit flow. @@ -18,7 +18,7 @@ impl FlowGraph { /// Initializes an flow network with vmax vertices and no edges. pub fn new(vmax: usize, emax_hint: usize) -> Self { Self { - graph: Graph::new(vmax, 2 * emax_hint), + graph: DirectedGraph::new(vmax, 2 * emax_hint), cap: Vec::with_capacity(2 * emax_hint), cost: Vec::with_capacity(2 * emax_hint), } @@ -31,7 +31,8 @@ impl FlowGraph { self.cap.push(rcap); self.cost.push(cost); self.cost.push(-cost); - self.graph.add_undirected_edge(u, v); + self.graph.add_edge(u, v); + self.graph.add_edge(v, u); } /// Dinic's algorithm to find the maximum flow from s to t where s != t. @@ -67,9 +68,9 @@ impl FlowGraph { q.push_back(s); while let Some(u) = q.pop_front() { for (e, v) in self.graph.adj_list(u) { - if dist[v] == Self::INF && flow[e] < self.cap[e] { - dist[v] = dist[u] + 1; - q.push_back(v); + if dist[*v] == Self::INF && flow[*e] < self.cap[*e] { + dist[*v] = dist[u] + 1; + q.push_back(*v); } } } @@ -92,11 +93,11 @@ impl FlowGraph { let mut df = 0; while let Some(&(e, v)) = adj[u].peek() { - let rem_cap = (self.cap[e] - flow[e]).min(f - df); - if rem_cap > 0 && dist[v] == dist[u] + 1 { - let cf = self.dinic_augment(v, t, rem_cap, dist, adj, flow); - flow[e] += cf; - flow[e ^ 1] -= cf; + let rem_cap = (self.cap[*e] - flow[*e]).min(f - df); + if rem_cap > 0 && dist[*v] == dist[u] + 1 { + let cf = self.dinic_augment(*v, t, rem_cap, dist, adj, flow); + flow[*e] += cf; + flow[(*e) ^ 1] -= cf; df += cf; if df == f { break; @@ -112,8 +113,7 @@ impl FlowGraph { pub fn min_cut(&self, dist: &[i64]) -> Vec { (0..self.graph.num_e()) .filter(|&e| { - let u = self.graph.endp[e ^ 1]; - let v = self.graph.endp[e]; + let (u, v) = self.graph.edges[e]; dist[u] < Self::INF && dist[v] == Self::INF }) .collect() @@ -125,15 +125,14 @@ impl FlowGraph { /// # Panics /// /// Panics if the flow or cost overflow a 64-bit signed integer. - pub fn mcf(&self, s: usize, t: usize) -> (i64, i64, Vec) { + pub fn min_cost_flow(&self, s: usize, t: usize) -> (i64, i64, Vec) { let mut pot = vec![0; self.graph.num_v()]; // Bellman-Ford deals with negative-cost edges at initialization. for _ in 1..self.graph.num_v() { for e in 0..self.graph.num_e() { if self.cap[e] > 0 { - let u = self.graph.endp[e ^ 1]; - let v = self.graph.endp[e]; + let (u, v) = self.graph.edges[e]; pot[v] = pot[v].min(pot[u] + self.cost[e]); } } @@ -142,11 +141,11 @@ impl FlowGraph { let mut flow = vec![0; self.graph.num_e()]; let (mut min_cost, mut max_flow) = (0, 0); loop { - let par = self.mcf_search(s, &flow, &mut pot); - if par[t] == None { + let par = self.min_cost_flow_search(s, &flow, &mut pot); + if par[t].is_none() { break; } - let (dc, df) = self.mcf_augment(t, &par, &mut flow); + let (dc, df) = self.min_cost_flow_augment(t, &par, &mut flow); min_cost += dc; max_flow += df; } @@ -155,7 +154,7 @@ impl FlowGraph { // Maintains Johnson's potentials to prevent negative-cost residual edges. // This allows running Dijkstra instead of the slower Bellman-Ford. - fn mcf_search(&self, s: usize, flow: &[i64], pot: &mut [i64]) -> Vec> { + fn min_cost_flow_search(&self, s: usize, flow: &[i64], pot: &mut [i64]) -> Vec> { let mut vis = vec![false; self.graph.num_v()]; let mut dist = vec![Self::INF; self.graph.num_v()]; let mut par = vec![None; self.graph.num_v()]; @@ -168,9 +167,9 @@ impl FlowGraph { vis[u] = true; pot[u] = dist[u]; for (e, v) in self.graph.adj_list(u) { - if dist[v] > dist[u] + self.cost[e] && flow[e] < self.cap[e] { - dist[v] = dist[u] + self.cost[e]; - par[v] = Some(e); + if dist[*v] > dist[u] + self.cost[*e] && flow[*e] < self.cap[*e] { + dist[*v] = dist[u] + self.cost[*e]; + par[*v] = Some(*e); } } } @@ -178,19 +177,24 @@ impl FlowGraph { } // Pushes flow along an augmenting path of minimum cost. - fn mcf_augment(&self, t: usize, par: &[Option], flow: &mut [i64]) -> (i64, i64) { + fn min_cost_flow_augment( + &self, + t: usize, + par: &[Option], + flow: &mut [i64], + ) -> (i64, i64) { let (mut dc, mut df) = (0, Self::INF); let mut u = t; while let Some(e) = par[u] { df = df.min(self.cap[e] - flow[e]); - u = self.graph.endp[e ^ 1]; + u = self.graph.edges[e ^ 1].1; } u = t; while let Some(e) = par[u] { flow[e] += df; flow[e ^ 1] -= df; dc += df * self.cost[e]; - u = self.graph.endp[e ^ 1]; + u = self.graph.edges[e ^ 1].1; } (dc, df) } @@ -218,7 +222,7 @@ mod test { graph.add_edge(2, 3, 7, 0, 8); graph.add_edge(1, 3, 7, 0, 10); - let (cost, flow, _) = graph.mcf(0, 3); + let (cost, flow, _) = graph.min_cost_flow(0, 3); assert_eq!(cost, 18); assert_eq!(flow, 10); } @@ -262,7 +266,7 @@ mod test { .enumerate() .filter(|&(_e, f)| f > 0) //map to u->v - .map(|(e, _f)| (graph.graph.endp[e ^ 1], graph.graph.endp[e])) + .map(|(e, _f)| (graph.graph.edges[e ^ 1].1, graph.graph.edges[e].1)) //leave out source and sink nodes .filter(|&(u, v)| u != source && v != sink) .collect::>(); diff --git a/src/graph/graph.rs b/src/graph/graph.rs new file mode 100644 index 0000000..7725b0d --- /dev/null +++ b/src/graph/graph.rs @@ -0,0 +1,155 @@ +//! Basic graph module without explicit support for deletion. +//! +//! # Panics +//! +//! All methods will panic if given an out-of-bounds element index. +use core::slice::Iter; +/// A compact graph representation. Edges are numbered in order of insertion. +/// Each adjacency list consists of all edges pointing out from a given vertex. +/// +use std::collections::HashSet; + +pub type AdjListIterator<'a> = Iter<'a, (usize, usize)>; + +pub struct DirectedGraph { + /// adjacency list. each vertex has a list of (edge index, destination vertex index) + pub adj_lists: Vec>, + /// Maps an edge id to the directed edge that it points to. + pub edges: Vec<(usize, usize)>, + /// edge weights + pub edge_weights: Vec, +} + +impl DirectedGraph { + /// Initializes a graph with vmax vertices and no edges. To reduce + /// unnecessary allocations, emax_hint should be close to the number of + /// edges that will be inserted. + pub fn new(vmax: usize, emax_hint: usize) -> Self { + Self { + adj_lists: vec![Vec::with_capacity(vmax); vmax], + edges: Vec::with_capacity(emax_hint), + edge_weights: Vec::with_capacity(emax_hint), + } + } + + /// Returns the number of vertices. + pub fn num_v(&self) -> usize { + self.adj_lists.len() + } + + /// Returns the number of edges + pub fn num_e(&self) -> usize { + self.edges.len() + } + + /// Adds a directed edge from u to v. + pub fn add_edge(&mut self, u: usize, v: usize) { + self.add_weighted_edge(u, v, 1i64); + } + + /// Adds a weighted directed edge from u to v. + pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { + self.edges.push((u, v)); + self.edge_weights.push(w); + self.adj_lists[u].push((self.edges.len() - 1, v)); + } + + /// If we think of each even-numbered vertex as a variable, and its + /// odd-numbered successor as its negation, then we can build the + /// implication graph corresponding to any 2-CNF formula. + /// Note that u||v == !u -> v == !v -> u. + pub fn add_two_sat_clause(&mut self, u: usize, v: usize) { + self.add_edge(u ^ 1, v); + self.add_edge(v ^ 1, u); + } + + /// Gets vertex u's adjacency list. + pub fn adj_list(&self, u: usize) -> AdjListIterator { + self.adj_lists[u].iter() + } +} + +pub struct UndirectedGraph { + /// adjacency list. each vertex has a list of (edge index, neighor vertex index) + pub adj_lists: Vec>, + /// Maps an edge id to vertices. + pub edges: Vec>, + /// edge weights + pub edge_weights: Vec, +} + +impl UndirectedGraph { + /// Initializes a graph with vmax vertices and no edges. To reduce + /// unnecessary allocations, emax_hint should be close to the number of + /// edges that will be inserted. + pub fn new(vmax: usize, emax_hint: usize) -> Self { + Self { + adj_lists: vec![Vec::with_capacity(vmax); vmax], + edges: Vec::with_capacity(emax_hint), + edge_weights: Vec::with_capacity(emax_hint), + } + } + + /// Returns the number of vertices. + pub fn num_v(&self) -> usize { + self.adj_lists.len() + } + + /// Returns the number of edges + pub fn num_e(&self) -> usize { + self.edges.len() + } + + /// Adds a edge from u to v. + pub fn add_edge(&mut self, u: usize, v: usize) { + self.add_weighted_edge(u, v, 1i64); + } + + /// Adds a weighted edge from u to v. + pub fn add_weighted_edge(&mut self, u: usize, v: usize, w: i64) { + let undirected_edge = HashSet::from([u, v]); + self.edges.push(undirected_edge); + self.edge_weights.push(w); + self.adj_lists[u].push((self.edges.len() - 1, v)); + self.adj_lists[v].push((self.edges.len() - 1, u)); + } + + /// Gets vertex u's adjacency list. + pub fn adj_list(&self, u: usize) -> AdjListIterator { + self.adj_lists[u].iter() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_adj_list() { + let mut graph = DirectedGraph::new(5, 6); + graph.add_edge(2, 3); + graph.add_edge(2, 4); + graph.add_edge(4, 1); + graph.add_edge(1, 2); + graph.add_edge(0, 2); + graph.add_edge(2, 0); + + let adj = graph.adj_list(2).collect::>(); + + for (e, v) in adj { + assert_eq!(*v, graph.edges[*e].1); + } + } + + #[test] + fn test_undirected_graph_basic() { + let mut graph = UndirectedGraph::new(5, 6); + graph.add_edge(2, 3); + graph.add_edge(4, 3); + graph.add_edge(1, 2); + graph.add_edge(1, 0); + + assert_eq!(4, graph.num_e()); + assert_eq!(5, graph.num_v()); + } +} diff --git a/src/graph/mod.rs b/src/graph/mod.rs deleted file mode 100644 index 41d8e30..0000000 --- a/src/graph/mod.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Basic graph module without explicit support for deletion. -//! -//! # Panics -//! -//! All methods will panic if given an out-of-bounds element index. -pub mod connectivity; -pub mod flow; -pub mod util; - -/// Represents a union of disjoint sets. Each set's elements are arranged in a -/// tree, whose root is the set's representative. -pub struct DisjointSets { - parent: Vec, -} - -impl DisjointSets { - /// Initializes disjoint sets containing one element each. - pub fn new(size: usize) -> Self { - Self { - parent: (0..size).collect(), - } - } - - /// Finds the set's representative. Do path compression along the way to make - /// future queries faster. - pub fn find(&mut self, u: usize) -> usize { - let pu = self.parent[u]; - if pu != u { - self.parent[u] = self.find(pu); - } - self.parent[u] - } - - /// Merges the sets containing u and v into a single set containing their - /// union. Returns true if u and v were previously in different sets. - pub fn merge(&mut self, u: usize, v: usize) -> bool { - let (pu, pv) = (self.find(u), self.find(v)); - self.parent[pu] = pv; - pu != pv - } -} - -/// A compact graph representation. Edges are numbered in order of insertion. -/// Each adjacency list consists of all edges pointing out from a given vertex. -pub struct Graph { - /// Maps a vertex id to the first edge in its adjacency list. - first: Vec>, - /// Maps an edge id to the next edge in the same adjacency list. - next: Vec>, - /// Maps an edge id to the vertex that it points to. - endp: Vec, -} - -impl Graph { - /// Initializes a graph with vmax vertices and no edges. To reduce - /// unnecessary allocations, emax_hint should be close to the number of - /// edges that will be inserted. - pub fn new(vmax: usize, emax_hint: usize) -> Self { - Self { - first: vec![None; vmax], - next: Vec::with_capacity(emax_hint), - endp: Vec::with_capacity(emax_hint), - } - } - - /// Returns the number of vertices. - pub fn num_v(&self) -> usize { - self.first.len() - } - - /// Returns the number of edges, double-counting undirected edges. - pub fn num_e(&self) -> usize { - self.endp.len() - } - - /// Adds a directed edge from u to v. - pub fn add_edge(&mut self, u: usize, v: usize) { - self.next.push(self.first[u]); - self.first[u] = Some(self.num_e()); - self.endp.push(v); - } - - /// An undirected edge is two directed edges. If edges are added only via - /// this funcion, the reverse of any edge e can be found at e^1. - pub fn add_undirected_edge(&mut self, u: usize, v: usize) { - self.add_edge(u, v); - self.add_edge(v, u); - } - - /// If we think of each even-numbered vertex as a variable, and its - /// odd-numbered successor as its negation, then we can build the - /// implication graph corresponding to any 2-CNF formula. - /// Note that u||v == !u -> v == !v -> u. - pub fn add_two_sat_clause(&mut self, u: usize, v: usize) { - self.add_edge(u ^ 1, v); - self.add_edge(v ^ 1, u); - } - - /// Gets vertex u's adjacency list. - pub fn adj_list(&self, u: usize) -> AdjListIterator { - AdjListIterator { - graph: self, - next_e: self.first[u], - } - } -} - -/// An iterator for convenient adjacency list traversal. -pub struct AdjListIterator<'a> { - graph: &'a Graph, - next_e: Option, -} - -impl<'a> Iterator for AdjListIterator<'a> { - type Item = (usize, usize); - - /// Produces an outgoing edge and vertex. - fn next(&mut self) -> Option { - self.next_e.map(|e| { - let v = self.graph.endp[e]; - self.next_e = self.graph.next[e]; - (e, v) - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_adj_list() { - let mut graph = Graph::new(5, 6); - graph.add_edge(2, 3); - graph.add_edge(2, 4); - graph.add_edge(4, 1); - graph.add_edge(1, 2); - graph.add_undirected_edge(0, 2); - - let adj = graph.adj_list(2).collect::>(); - - assert_eq!(adj, vec![(5, 0), (1, 4), (0, 3)]); - for (e, v) in adj { - assert_eq!(v, graph.endp[e]); - } - } -} diff --git a/src/graph/util.rs b/src/graph/util.rs index b7a3817..e37ec91 100644 --- a/src/graph/util.rs +++ b/src/graph/util.rs @@ -1,8 +1,17 @@ -use super::{DisjointSets, Graph}; -use crate::graph::AdjListIterator; +use super::disjoint_set::DisjointSets; +use super::graph::{AdjListIterator, DirectedGraph, UndirectedGraph}; use std::cmp::Reverse; +use std::f32::consts::E; -impl Graph { +impl DirectedGraph { + // Helper function used by euler_path. Note that we can't use a for-loop + // that would consume the adjacency list as recursive calls may need it. + fn euler_recurse(u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { + while let Some((e, v)) = adj[u].next() { + Self::euler_recurse(*v, adj, edges); + edges.push(*e); + } + } /// Finds the sequence of edges in an Euler path starting from u, assuming /// it exists and that the graph is directed. Undefined behavior if this /// precondition is violated. To extend this to undirected graphs, maintain @@ -12,37 +21,14 @@ impl Graph { .map(|u| self.adj_list(u)) .collect::>(); let mut edges = Vec::with_capacity(self.num_e()); - self.euler_recurse(u, &mut adj_iters, &mut edges); + Self::euler_recurse(u, &mut adj_iters, &mut edges); edges.reverse(); edges } - // Helper function used by euler_path. Note that we can't use a for-loop - // that would consume the adjacency list as recursive calls may need it. - fn euler_recurse(&self, u: usize, adj: &mut [AdjListIterator], edges: &mut Vec) { - while let Some((e, v)) = adj[u].next() { - self.euler_recurse(v, adj, edges); - edges.push(e); - } - } - - /// Kruskal's minimum spanning tree algorithm on an undirected graph. - pub fn min_spanning_tree(&self, weights: &[i64]) -> Vec { - assert_eq!(self.num_e(), 2 * weights.len()); - let mut edges = (0..weights.len()).collect::>(); - edges.sort_unstable_by_key(|&e| weights[e]); - - let mut components = DisjointSets::new(self.num_v()); - edges - .into_iter() - .filter(|&e| components.merge(self.endp[2 * e], self.endp[2 * e + 1])) - .collect() - } - // Single-source shortest paths on a directed graph with non-negative weights - pub fn dijkstra(&self, weights: &[u64], u: usize) -> Vec { - assert_eq!(self.num_e(), weights.len()); - let mut dist = vec![u64::max_value(); weights.len()]; + pub fn dijkstra(&self, u: usize) -> Vec { + let mut dist = vec![u64::max_value(); self.edge_weights.len()]; let mut heap = std::collections::BinaryHeap::new(); dist[u] = 0; @@ -50,10 +36,10 @@ impl Graph { while let Some((Reverse(dist_u), u)) = heap.pop() { if dist[u] == dist_u { for (e, v) in self.adj_list(u) { - let dist_v = dist_u + weights[e]; - if dist[v] > dist_v { - dist[v] = dist_v; - heap.push((Reverse(dist_v), v)); + let dist_v = dist_u + self.edge_weights[*e] as u64; + if dist[*v] > dist_v { + dist[*v] = dist_v; + heap.push((Reverse(dist_v), *v)); } } } @@ -74,8 +60,51 @@ impl Graph { adj_iters, } } + // this does not check for overflow + // you can have negative edge weights, but you also need to have no negative cycles + pub fn floyd_warshall(&self) -> Vec> { + let numv = self.num_v(); + let mut dist = vec![vec![i64::MAX; numv]; numv]; + + for v_idx in 0..numv { + dist[v_idx][v_idx] = 0; + } + + for (idx, edge) in self.edges.iter().enumerate() { + dist[edge.0][edge.1] = self.edge_weights[idx]; + } + + for k in 0..numv { + for i in 0..numv { + for j in 0..numv { + if dist[i][k] < i64::MAX && dist[k][j] < i64::MAX { + if dist[i][j] > dist[i][k] + dist[k][j] { + dist[i][j] = dist[i][k] + dist[k][j]; + } + } + } + } + } + dist + } } +impl UndirectedGraph { + /// Kruskal's minimum spanning tree algorithm on an undirected weighted graph. + pub fn min_spanning_tree(&self) -> Vec { + let mut edges = (0..self.edge_weights.len()).collect::>(); + edges.sort_unstable_by_key(|&e| self.edge_weights[e]); + + let mut components = DisjointSets::new(self.num_v()); + edges + .into_iter() + .filter(|&e| { + let edge_vec = Vec::from_iter(&self.edges[e]); + components.merge(*edge_vec[0], *edge_vec[1]) + }) + .collect() + } +} pub struct DfsIterator<'a> { visited: Vec, stack: Vec, @@ -92,10 +121,10 @@ impl<'a> Iterator for DfsIterator<'a> { loop { let &u = self.stack.last()?; for (e, v) in self.adj_iters[u].by_ref() { - if !self.visited[v] { - self.visited[v] = true; - self.stack.push(v); - return Some((e, v)); + if !self.visited[*v] { + self.visited[*v] = true; + self.stack.push(*v); + return Some((*e, *v)); } } self.stack.pop(); @@ -109,7 +138,7 @@ mod test { #[test] fn test_euler() { - let mut graph = Graph::new(3, 4); + let mut graph = DirectedGraph::new(3, 4); graph.add_edge(0, 1); graph.add_edge(1, 0); graph.add_edge(1, 2); @@ -120,33 +149,31 @@ mod test { #[test] fn test_min_spanning_tree() { - let mut graph = Graph::new(3, 6); - graph.add_undirected_edge(0, 1); - graph.add_undirected_edge(1, 2); - graph.add_undirected_edge(2, 0); - let weights = [7, 3, 5]; - - let mst = graph.min_spanning_tree(&weights); - let mst_cost = mst.iter().map(|&e| weights[e]).sum::(); + let mut graph = UndirectedGraph::new(3, 6); + graph.add_weighted_edge(0, 1, 7); + graph.add_weighted_edge(1, 2, 3); + graph.add_weighted_edge(2, 0, 5); + + let mst = graph.min_spanning_tree(); + let mst_cost = mst.iter().map(|&e| graph.edge_weights[e]).sum::(); assert_eq!(mst, vec![1, 2]); assert_eq!(mst_cost, 8); } #[test] fn test_dijkstra() { - let mut graph = Graph::new(3, 3); - graph.add_edge(0, 1); - graph.add_edge(1, 2); - graph.add_edge(2, 0); - let weights = [7, 3, 5]; + let mut graph = DirectedGraph::new(3, 3); + graph.add_weighted_edge(0, 1, 7); + graph.add_weighted_edge(1, 2, 3); + graph.add_weighted_edge(2, 0, 5); - let dist = graph.dijkstra(&weights, 0); + let dist = graph.dijkstra(0); assert_eq!(dist, vec![0, 7, 10]); } #[test] fn test_dfs() { - let mut graph = Graph::new(4, 6); + let mut graph = DirectedGraph::new(4, 6); graph.add_edge(0, 2); graph.add_edge(2, 0); graph.add_edge(1, 2); @@ -159,12 +186,12 @@ mod test { .chain(graph.dfs(dfs_root).map(|(_, v)| v)) .collect::>(); - assert_eq!(dfs_traversal, vec![2, 3, 0, 1]); + assert_eq!(dfs_traversal, vec![2, 0, 1, 3]); } #[test] fn test_dfs2() { - let mut graph = Graph::new(5, 6); + let mut graph = DirectedGraph::new(5, 6); graph.add_edge(0, 2); graph.add_edge(2, 1); graph.add_edge(1, 0); @@ -177,16 +204,17 @@ mod test { .chain(graph.dfs(dfs_root).map(|(_, v)| v)) .collect::>(); - assert_eq!(dfs_traversal, vec![0, 3, 4, 2, 1]); + assert_eq!(dfs_traversal, vec![0, 2, 1, 3, 4]); } #[test] fn test_dfs_space_complexity() { let num_v = 20; - let mut graph = Graph::new(num_v, 0); + let mut graph = DirectedGraph::new(num_v, 0); for i in 0..num_v { for j in 0..num_v { - graph.add_undirected_edge(i, j); + graph.add_edge(i, j); + graph.add_edge(j, i); } } @@ -204,4 +232,24 @@ mod test { assert_eq!(num_v, dfs_check.len()); assert_eq!(num_v - 1, dfs_check[num_v - 1]); } + + #[test] + fn test_floyd_warshall() { + let num_v = 8; + let mut graph = DirectedGraph::new(num_v, 10); + graph.add_weighted_edge(0, 1, 1); + graph.add_weighted_edge(1, 2, 2); + graph.add_weighted_edge(1, 4, 4); + graph.add_weighted_edge(2, 5, 3); + graph.add_weighted_edge(4, 3, 6); + graph.add_weighted_edge(5, 4, 10); + graph.add_weighted_edge(3, 6, 2); + graph.add_weighted_edge(4, 6, 7); + graph.add_weighted_edge(6, 7, 2); + graph.add_weighted_edge(5, 7, 9); + + let dist = graph.floyd_warshall(); + + assert_eq!(dist[0][7], 14i64); + } } diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 0000000..4912f7e --- /dev/null +++ b/src/math.rs @@ -0,0 +1,3 @@ +pub mod division; +pub mod fft; +pub mod num; diff --git a/src/math/mod.rs b/src/math/division.rs similarity index 76% rename from src/math/mod.rs rename to src/math/division.rs index 42127cc..1851a56 100644 --- a/src/math/mod.rs +++ b/src/math/division.rs @@ -1,6 +1,6 @@ -//! Number-theoretic utilities for contest problems. -pub mod fft; -pub mod num; +use super::num; + +//Number-theoretic utilities for contest problems. /// Finds (d, coef_a, coef_b) such that d = gcd(a, b) = a * coef_a + b * coef_b. pub fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) { @@ -86,6 +86,37 @@ pub fn is_prime(n: i64) -> bool { } } +// Steins algorithm: Stein’s algorithm or binary GCD algorithm is an algorithm that +// computes the greatest common divisor of two non-negative integers. +// Stein’s algorithm replaces division with arithmetic shifts, +// comparisons, and subtraction. +// Use Stein's algorithm +fn gcd_stein(mut m: u64, mut n: u64) -> u64 { + if m == 0 || n == 0 { + return m | n; + } + + // find common factors of 2 + let shift = (m | n).trailing_zeros(); + m >>= m.trailing_zeros(); + n >>= n.trailing_zeros(); + + while m != n { + if m > n { + m -= n; + m >>= m.trailing_zeros(); + } else { + n -= m; + n >>= n.trailing_zeros(); + } + } + m << shift +} + +// Pollard's rho algorithm is an algorithm for integer factorization. +// It was invented by John Pollard in 1975.[1] It uses only a small amount of space, +// and its expected running time is proportional to the square root of the smallest +// prime factor of the composite number being factorized. fn pollard_rho(n: i64) -> i64 { for a in 1..n { let f = |x| pos_mod(mod_mul(x, x, n) + a, n); @@ -177,4 +208,19 @@ mod test { vec![11, 13, 17, 19, 29, 37, 41, 43, 61, 97, 109, 127] ); } + + #[test] + fn test_steins() { + let (a, b) = (14, 35); + let d = gcd_stein(a, b); + assert_eq!(d, 7u64); + + let (p1, p2) = (393919, 919393); + let d1 = gcd_stein(p1, p2); + assert_eq!(d1, 1u64); + + let (p3, p4) = (679389209, 696729599); + let d2 = gcd_stein(p3, p4); + assert_eq!(d2, 1u64); + } } diff --git a/src/order.rs b/src/order.rs index 6ba82d9..798f5ac 100644 --- a/src/order.rs +++ b/src/order.rs @@ -5,10 +5,7 @@ /// # Example /// /// ``` -/// use contest_algorithms::order::asserting_cmp; -/// let mut vec = vec![4.5, -1.7, 1.2]; -/// vec.sort_unstable_by(asserting_cmp); -/// assert_eq!(vec, vec![-1.7, 1.2, 4.5]); + /// ``` pub fn asserting_cmp(a: &T, b: &T) -> std::cmp::Ordering { a.partial_cmp(b).expect("Comparing incomparable elements") diff --git a/src/range_query.rs b/src/range_query.rs new file mode 100644 index 0000000..3f3767f --- /dev/null +++ b/src/range_query.rs @@ -0,0 +1,4 @@ +pub mod dynamic_arq; +pub mod specs; +pub mod sqrt_decomp; +pub mod static_arq; diff --git a/src/range_query/README.md b/src/range_query/README.md index aeb953a..c4681c0 100644 --- a/src/range_query/README.md +++ b/src/range_query/README.md @@ -1,3 +1,3 @@ # Associative Range Query (ARQ) and Mo's Algorithm -For more information on Associative Range Query, you may research "segment trees" in the programming contest literature. My implementation is more general than usual; for more information on it, please see my [blog post on Codeforces](https://codeforces.com/blog/entry/68419). +For more information on Associative Range Query, you may research "segment trees" in the programming contest literature. My implementation is more general than usual; for more information on it, please see ebtech's [blog post on Codeforces](https://codeforces.com/blog/entry/68419). diff --git a/src/range_query/dynamic_arq.rs b/src/range_query/dynamic_arq.rs index f33eaaf..d06d01d 100644 --- a/src/range_query/dynamic_arq.rs +++ b/src/range_query/dynamic_arq.rs @@ -1,6 +1,6 @@ //! Associative Range Query Tree with dynamic allocation, supporting sparse //! initialization and persistence -use super::ArqSpec; +use super::specs::ArqSpec; pub struct DynamicArqNode { val: T::S, diff --git a/src/range_query/static_arq.rs b/src/range_query/static_arq.rs index 14a9331..3b045db 100644 --- a/src/range_query/static_arq.rs +++ b/src/range_query/static_arq.rs @@ -1,5 +1,5 @@ //! Associative Range Query Tree -use super::ArqSpec; +use super::specs::ArqSpec; /// Colloquially known as a "segtree" in the sport programming literature, it /// represents a sequence of elements a_i (0 <= i < size) from a monoid (S, +) diff --git a/src/range_query/mod.rs b/src/range_query/tests.rs similarity index 100% rename from src/range_query/mod.rs rename to src/range_query/tests.rs diff --git a/src/string_proc.rs b/src/string_proc.rs index 888bf67..da0583e 100644 --- a/src/string_proc.rs +++ b/src/string_proc.rs @@ -60,6 +60,7 @@ impl<'a, C: Eq> Matcher<'a, C> { /// # Example /// /// ``` + /* /// use contest_algorithms::string_proc::Matcher; /// let byte_string: &[u8] = b"hello"; /// let utf8_string: &str = "hello"; @@ -71,6 +72,8 @@ impl<'a, C: Eq> Matcher<'a, C> { /// /// let vec_int = vec![4, -3, 1]; /// let match_from_ints = Matcher::new(&vec_int); + /// + */ /// ``` /// /// # Panics @@ -327,15 +330,6 @@ pub fn palindromes(text: &[impl Eq]) -> Vec { /// # Example /// /// ``` -/// use contest_algorithms::string_proc::z_algorithm; -/// let z = z_algorithm(b"ababbababbabababbabababbababbaba"); -/// assert_eq!( -/// z, -/// vec![ -/// 32, 0, 2, 0, 0, 9, 0, 2, 0, 0, 4, 0, 9, 0, 2, 0, 0, 4, 0, 13, 0, 2, -/// 0, 0, 8, 0, 2, 0, 0, 3, 0, 1, -/// ], -/// ); /// ``` pub fn z_algorithm(text: &[impl Eq]) -> Vec { let n = text.len(); @@ -356,6 +350,40 @@ pub fn z_algorithm(text: &[impl Eq]) -> Vec { z } +///Levenshtein distance :is a string metric for measuring the difference between two sequences. +/// Informally, the Levenshtein distance between two words is the minimum number of single-character edits +/// (insertions, deletions or substitutions) required to change one word into the other. +/// It is named after the Soviet mathematician Vladimir Levenshtein, who considered this distance in 1965. +/// +/// for all i and j, d[i,j] will hold the Levenshtein distance between +/// the first i characters of s and the first j characters of t; starting at 1. +/// in other words, m is length of s, n is length of t. +/// note that dist has (m+1)x(n+1) values, +pub fn levenshtein_distance(s: &str, m: usize, t: &str, n: usize) -> Vec> { + let mut dist = vec![vec![0u64; n + 1]; m + 1]; + for i in 0..m + 1 { + dist[i][0] = i as u64; + } + for j in 0..n + 1 { + dist[0][j] = j as u64; + } + + for j in 1..n + 1 { + for i in 1..m + 1 { + dist[i][j] = dist[i - 1][j - 1] + + if s.chars().nth(i - 1) != t.chars().nth(j - 1) { + 1u64 + } else { + 0u64 + }; + + dist[i][j] = std::cmp::min(dist[i][j], dist[i - 1][j] + 1); + dist[i][j] = std::cmp::min(dist[i][j], dist[i][j - 1] + 1); + } + } + + dist +} #[cfg(test)] mod test { use super::*; @@ -432,4 +460,14 @@ mod test { assert_eq!(pal_len, vec![1, 0, 1, 0, 3, 0, 5, 0, 3, 0, 1]); } + + #[test] + fn test_levenshtein_distance() { + let text1 = "Saturday"; + let text2 = "Sunday"; + + let d = levenshtein_distance(text1, 8, text2, 6); + + assert_eq!(d[8][6], 3); + } } diff --git a/tests/codeforces343d.rs b/tests/codeforces343d.rs index 94b911a..c0d200d 100644 --- a/tests/codeforces343d.rs +++ b/tests/codeforces343d.rs @@ -2,10 +2,11 @@ //! To make a self-contained file for contest submission, dump each desired //! module's contents directly here instead of the use statements. //! Also, use the commented code in main() to employ standard I/O. -extern crate contest_algorithms; -use contest_algorithms::graph::Graph; -use contest_algorithms::range_query::{specs::AssignSum, StaticArq}; -use contest_algorithms::scanner::Scanner; +extern crate contest_llamas; +use contest_llamas::graph::graph::DirectedGraph; +use contest_llamas::range_query::specs::AssignSum; +use contest_llamas::range_query::static_arq::StaticArq; +use contest_llamas::scanner::Scanner; use std::io; const SAMPLE_INPUT: &[u8] = b"\ @@ -40,7 +41,7 @@ const SAMPLE_OUTPUT: &[u8] = b"\ "; fn dfs( - graph: &Graph, + graph: &DirectedGraph, u: usize, l: &mut [usize], r: &mut [usize], @@ -51,9 +52,9 @@ fn dfs( l[u] = *time; for (_, v) in graph.adj_list(u) { - if l[v] == 0 { - p[v] = l[u]; - dfs(graph, v, l, r, p, time); + if l[*v] == 0 { + p[*v] = l[u]; + dfs(graph, *v, l, r, p, time); } } @@ -62,11 +63,12 @@ fn dfs( fn solve(scan: &mut Scanner, out: &mut W) { let n = scan.token::(); - let mut tree = Graph::new(n, 2 * (n - 1)); + let mut tree = DirectedGraph::new(n, 2 * (n - 1)); for _ in 1..n { let u = scan.token::() - 1; let v = scan.token::() - 1; - tree.add_undirected_edge(u, v); + tree.add_edge(u, v); + tree.add_edge(v, u); } let mut l = vec![0; n];