Cod sursa(job #3190713)

Utilizator mario_deaconescuMario Deaconescu mario_deaconescu Data 7 ianuarie 2024 20:48:01
Problema Cc Scor 0
Compilator cpp-64 Status done
Runda Arhiva de probleme Marime 24.34 kb




#include <vector>
#include <cstdio>
#include <queue>
#include <functional>
#include <cmath>
#include <stack>
#include <stdio.h>
#include <optional>
#include <iostream>
#include <limits>
#include <climits>

#include <unordered_set>

template<class T = std::nullptr_t, class U = std::nullptr_t, bool directed = false, bool storeParents = true>
class Graph {
public:
    class Node;

    class Edge;

private:
    std::vector<Node *> nodes;
public:
    struct Edge {
        Node *to;
        U data;

        Edge(Node *to) : to(to) {}

        Edge(Node *to, const U &data) : to(to), data(data) {}
    };

    struct EdgePair {
        std::pair<size_t, size_t> pair;
        U data;

        EdgePair(const std::pair<size_t, size_t> &pair) : pair(pair) {}

        EdgePair(const std::pair<size_t, size_t> &pair, const U &data) : pair(pair), data(data) {}

        bool operator==(const EdgePair &other) const {
            return pair == other.pair && data == other.data;
        }

        bool operator!=(const EdgePair &other) const {
            return pair != other.pair || data != other.data;
        }

        bool operator<(const EdgePair &other) const {
            return data < other.data;
        }
    };

    class Node {
    private:
        size_t inDegree = 0;
        size_t outDegree = 0;
        size_t id;
        T data;
        std::vector<Edge> neighbors;
        std::vector<Edge> parents;
    public:
        explicit Node(const size_t &id) : id(id) {}

        Node(const size_t &id, const T &data) : id(id), data(data) {}

        Node(const Node &other) : id(other.id), data(other.data), neighbors(other.neighbors) {}

        Node(Node &&other) noexcept: id(other.id), data(std::move(other.data)), neighbors(std::move(other.neighbors)) {}

        inline bool operator==(const Node &other) const {
            return data == other.data;
        }

        inline bool operator!=(const Node &other) const {
            return data != other.data;
        }

        inline void addNeighbour(Node *neighbor) {
            neighbors.push_back(Edge(neighbor));
            ++neighbor->inDegree;
            outDegree++;
            if (directed) {
                if (storeParents)
                    neighbor->parents.push_back(Edge(this));
            } else {
                neighbor->neighbors.push_back(Edge(this));
                ++neighbor->outDegree;
                inDegree++;
            }
        }

        inline void addNeighbour(Node *neighbor, const U &edgeData) {
            neighbors.push_back(Edge(neighbor, edgeData));
            ++neighbor->inDegree;
            outDegree++;
            if (directed) {
                if (storeParents)
                    neighbor->parents.push_back(Edge(this, edgeData));
            } else {
                neighbor->neighbors.push_back(Edge(this, edgeData));
                ++neighbor->outDegree;
                inDegree++;
            }
        }

        inline std::optional<Edge> getNeighbor(const size_t &neighbourId) const {
            // Find the neighbor with the given id
            for (const Edge &edge: neighbors) {
                if (edge.to->getId() == neighbourId) {
                    return edge;
                }
            }
            return {};
        }

        inline void removeNeighbor(const size_t &neighbourId) {
            // Find the neighbor with the given id
            Graph::Node* neighbor = nullptr;
            if(const auto temp = getNeighbor(neighbourId); temp.has_value()){
                neighbor = temp->to;
            }else {
                return;
            }

            for (size_t i = 0; i < neighbors.size(); i++) {
                if (neighbors[i].to->getId() == neighbourId) {
                    neighbors.erase(neighbors.begin() + i);
                    break;
                }
            }
            --outDegree;
            --neighbor->inDegree;
            if (directed) {
                if (storeParents){
                    // Remove this node from the neighbor's parents
                    for (size_t i = 0; i < neighbor->parents.size(); i++) {
                        if (neighbor->parents[i].to->getId() == id) {
                            neighbor->parents.erase(neighbor->parents.begin() + i);
                            break;
                        }
                    }
                }
            } else {
                for (size_t i = 0; i < neighbor->neighbors.size(); i++) {
                    if (neighbor->neighbors[i].to->getId() == id) {
                        neighbor->neighbors.erase(neighbor->neighbors.begin() + i);
                        break;
                    }
                }
                --neighbor->outDegree;
                --inDegree;
            }
        }

        inline const std::vector<Edge> &getNeighbors() const {
            return neighbors;
        }

        inline const T &getData() const {
            return data;
        }

        inline T &getData() {
            return data;
        }

        inline const size_t &getId() const {
            return id;
        }

        inline const size_t &getInDegree() const {
            return inDegree;
        }

        inline const size_t &getOutDegree() const {
            return outDegree;
        }

        inline const std::vector<Edge> &getParents() const {
            return parents;
        }

        void removeNeighbor(Node *neighbor) {
            for (size_t i = 0; i < neighbors.size(); i++) {
                if (neighbors[i].to == neighbor) {
                    neighbors.erase(neighbors.begin() + i);
                    break;
                }
            }
            neighbor->inDegree--;
            outDegree--;
            if (directed) {
                if (storeParents)
                    for (size_t i = 0; i < neighbor->parents.size(); i++) {
                        if (neighbor->parents[i].to == this) {
                            neighbor->parents.erase(neighbor->parents.begin() + i);
                            break;
                        }
                    }
            } else {
                for (size_t i = 0; i < neighbor->neighbors.size(); i++) {
                    if (neighbor->neighbors[i].to == this) {
                        neighbor->neighbors.erase(neighbor->neighbors.begin() + i);
                        break;
                    }
                }
                neighbor->outDegree--;
                inDegree--;
            }
        }
    };

    Graph() = default;

    Graph(const size_t &totalNodes, const std::vector<EdgePair> &edges, const T &defaultValue) {
        nodes.reserve(totalNodes);
        for (size_t id = 0; id < totalNodes; id++) {
            nodes.push_back(new Node(id, defaultValue));
        }
        for (const auto &edge: edges) {
            const auto &from = edge.pair.first;
            const auto &to = edge.pair.second;
            if (from >= nodes.size()) {
                nodes.resize(from + 1);
            }
            if (to >= nodes.size()) {
                nodes.resize(to + 1);
            }
            nodes[from]->addNeighbour(nodes[to], edge.data);
        }
    }

    Graph(const size_t &totalNodes, const std::vector<EdgePair> &edges) {
        nodes.reserve(totalNodes);
        for (size_t id = 0; id < totalNodes; id++) {
            nodes.push_back(new Node(id));
        }
        for (const auto &edge: edges) {
            const auto &from = edge.pair.first;
            const auto &to = edge.pair.second;
            if (from >= nodes.size()) {
                nodes.resize(from + 1);
            }
            if (to >= nodes.size()) {
                nodes.resize(to + 1);
            }
            nodes[from]->addNeighbour(nodes[to], edge.data);
        }
    }

    Graph(const Graph &other) = delete;

    Graph(Graph &&other) noexcept: nodes(std::move(other.nodes)) {}

    ~Graph() {
        for (auto node: nodes) {
            delete node;
        }
    }

    const size_t size() const {
        return nodes.size();
    }

    typename std::vector<Node *>::iterator begin() {
        return nodes.begin();
    }

    typename std::vector<Node *>::iterator end() {
        return nodes.end();
    }

    typename std::vector<Node *>::const_iterator begin() const {
        return nodes.begin();
    }

    typename std::vector<Node *>::const_iterator end() const {
        return nodes.end();
    }

    inline Node *operator[](const size_t &index) {
        return nodes[index];
    }

    static std::vector<EdgePair> convertEdges(const std::vector<std::vector<int>> &edges) {
        std::vector<EdgePair> result;
        result.reserve(edges.size());
        for (const auto &edge: edges) {
            result.emplace_back(std::make_pair(edge[0], edge[1]));
        }
        return result;
    }

    static std::vector<EdgePair> convertFromNeighborList(const std::vector<std::vector<int>> &neighborList) {
        std::vector<EdgePair> result;
        result.reserve(neighborList.size());
        for (size_t i = 0; i < neighborList.size(); i++) {
            for (const auto &neighbor: neighborList[i]) {
                result.emplace_back(std::make_pair(i, neighbor));
            }
        }
        return result;
    }

    template<class Distance>
    Graph getMinimumSpanningTree_Kruskal(const std::function<double(const U &)> &getWeight, Distance &distance) const {
        distance = 0;
        if (nodes.empty()) {
            return Graph();
        }

        // Create a priority queue to store edges
        auto compare = [&getWeight](const EdgePair &a, const EdgePair &b) {
            return getWeight(a.data) > getWeight(b.data);
        };

        auto totalEdges = getEdges();
        std::priority_queue<EdgePair, std::vector<EdgePair>, decltype(compare)> pq(compare);
        for (const EdgePair &edge: totalEdges) {
            pq.push(edge);
        }
        // Peform Kruskal's algorithm
        std::vector<EdgePair> result;
        result.reserve(nodes.size() - 1);
        size_t components = nodes.size();

        std::vector<size_t> parent(nodes.size());
        for (size_t i = 0; i < nodes.size(); ++i) {
            parent[i] = i;
        }

        const std::function<size_t(const size_t &element)> findParent = [&parent, &findParent](const size_t &element) {
            if (parent[element] == element) {
                return element;
            }
            return parent[element] = findParent(parent[element]);
        };

        const auto merge = [&parent, &findParent, &components](const size_t &from, const size_t &to) {
            const auto fromParent = findParent(from);
            const auto toParent = findParent(to);
            if (fromParent != toParent) {
                parent[fromParent] = toParent;
                --components;
            }
        };

        while (!pq.empty() && components > 1) {
            const EdgePair &edge = pq.top();
            pq.pop();
            size_t from = findParent(edge.pair.first);
            size_t to = findParent(edge.pair.second);
            const auto weight = getWeight(edge.data);
            if (from != to) {
                if (weight == std::numeric_limits<Distance>::max()) {
                    distance = std::numeric_limits<Distance>::max();
                    return {};
                }
                merge(from, to);
                result.push_back(edge);
                distance += weight;
            }
        }

        return {nodes.size(), result};
    }

    template<class Distance>
    Graph getMinimumSpanningTree_Kruskal(const std::function<Distance(const U &)> &getWeight) const {
        Distance distance = 0;
        return getMinimumSpanningTree_Kruskal<Distance>(getWeight, distance);
    }

    template<class Distance>
    Graph getMinimumSpanningTree_Prim(
            const std::function<bool &(Graph::Node *)> &getVisited,
            const std::function<Distance(const U &)> &getWeight,
            Distance &distance) const {
        distance = 0;
        if (nodes.empty()) {
            return Graph();
        }
        // Create a priority queue to store edges
        auto compare = [&getWeight](const Graph::EdgePair &a,
                                    const Graph::EdgePair &b) {
            return getWeight(a.data) > getWeight(b.data);
        };

        std::priority_queue<Graph::EdgePair, std::vector<Graph::EdgePair>, decltype(compare)> pq(
                compare);

        // Start with the first node
        for (const auto &edge: nodes[0]->getNeighbors()) {
            pq.push(Graph::EdgePair(std::make_pair(0, edge.to->getId()), edge.data));
        }

        // Peform Prim's algorithm
        std::vector<Graph::EdgePair> result;
        result.reserve(nodes.size() - 1);

        getVisited(nodes[0]) = true;

        while (!pq.empty() && result.size() < nodes.size() - 1) {
            const Graph::EdgePair edge = pq.top();
            pq.pop();

            if (!getVisited(nodes[edge.pair.second])) {
                getVisited(nodes[edge.pair.second]) = true;
                result.push_back(edge);
                distance += getWeight(edge.data);

                // Add the neighbors of the newly added node to the priority queue
                for (const auto &neighbor: nodes[edge.pair.second]->getNeighbors()) {
                    if (!getVisited(neighbor.to)) {
                        pq.push(Graph::EdgePair(
                                std::make_pair(edge.pair.second, neighbor.to->getId()), neighbor.data));
                    }
                }
            }
        }

        return {nodes.size(), result};
    }

    template<class Distance>
    Graph getMinimumSpanningTree_Prim(
            const std::function<bool &(Graph::Node *)> &getVisited,
            const std::function<Distance(const U &)> &getWeight) const {
        Distance distance = 0;
        return getMinimumSpanningTree_Prim<Distance>(getVisited, getWeight, distance);
    }

    void dfs_from(const size_t &from,
                  const std::function<bool &(Node *)> &getVisited,
                  const std::function<void(Node *)> &nodeCallback,
                  const std::function<void(Node *, Edge)> &edgeCallback) {
        std::stack<Node *> stack;
        stack.push(nodes[from]);
        getVisited(nodes[from]) = true;
        while (!stack.empty()) {
            auto node = stack.top();
            stack.pop();
            nodeCallback(node);
            for (auto neighbor: node->getNeighbors()) {
                if (!getVisited(neighbor.to)) {
                    edgeCallback(node, neighbor);
                    stack.push(neighbor.to);
                    getVisited(neighbor.to) = true;
                }
            }
        }
    }

    void dfs(const std::function<bool &(Node *)> &getVisited,
             const std::function<void(const size_t&, Node *)> &nodeCallback,
             const std::function<void(const size_t&, Node *, Edge)> &edgeCallback) {
        size_t components = 0;
        for (auto root: nodes) {
            if (getVisited(root)) {
                continue;
            }
            dfs_from(root->getId(),
                     getVisited,
                     [=](Graph::Node* node){nodeCallback(components, node);},
                     [=](Graph::Node* node, Graph::Edge edge){edgeCallback(components, node, edge);});
            ++components;
        }
    }

    void bfs_from(const size_t &from,
                  const std::function<bool &(Node *)> &getVisited,
                  const std::function<void(Node*)> &nodeCallback,
                  const std::function<void(Node*, Edge)> &edgeCallback) {
        std::queue<Node *> queue;
        queue.push(nodes[from]);
        getVisited(nodes[from]) = true;
        while (!queue.empty()) {
            auto node = queue.front();
            queue.pop();
            nodeCallback(node);
            for (auto neighbor: node->getNeighbors()) {
                if (!getVisited(neighbor.to)) {
                    edgeCallback(node, neighbor);
                    queue.push(neighbor.to);
                    getVisited(neighbor.to) = true;
                }
            }
        }
    }

    void bfs(const std::function<bool &(Node *)> &getVisited,
             const std::function<void(const size_t&, Node*)> &nodeCallback,
             const std::function<void(const size_t&, Node*, Edge)> &edgeCallback) {
        size_t components = 0;
        for (auto root: nodes) {
            if (getVisited(root)) {
                continue;
            }
            bfs_from(root->getId(),
                     getVisited,
                     [=](Graph::Node* node){nodeCallback(components, node);},
                     [=](Graph::Node* node, Graph::Edge edge){edgeCallback(components, node, edge);});
            ++components;
        }
    }

    int bfs(const size_t& source,const size_t& destination,std::vector<std::vector<int>>& capacity,std::vector<std::vector<int>>& flow, std::vector<int>& parent) {
        std::deque<int> queue;
        std::vector<int> visited(destination+1,0);

        parent.assign(destination+1,0);

        queue.push_back(source);
        visited[source]=1;

        while (!queue.empty()){
            int node=queue.front();
            queue.pop_front();
            if (node==destination)
                break;
            for (auto neighbor : nodes[node]->getNeighbors()) {
                int neighborId = neighbor.to->getId();
                if (!visited[neighborId] && capacity[node][neighborId]-flow[node][neighborId]>0) {
                    queue.push_back(neighborId);
                    parent[neighborId]=node;
                    visited[neighborId]=1;
                }
            }
        }
        if (parent[destination]!=0)
            return 1;
        return 0;
    }

    int edmondKarp(const size_t& source, const size_t& destination,std::vector<std::vector<int>>& capacity,std::vector<std::vector<int>>& flow) {
        int totalFlow=0;
        int minFlow;
        std::vector<int> parent;

        while (bfs(source,destination,capacity,flow,parent)){
            minFlow=INT_MAX;
            int i=destination;
            while (i!=0) {
                if (capacity[parent[i]][i]-flow[parent[i]][i]<minFlow)
                    minFlow=capacity[parent[i]][i]-flow[parent[i]][i];
                i=parent[i];
            }

            i=destination;
            while (i!=0) {
                flow[parent[i]][i]+=minFlow;
                flow[i][parent[i]]-=minFlow;
                i=parent[i];
            }
            totalFlow+=minFlow;
        }
        return totalFlow;
    }

    std::vector<typename Graph::EdgePair> getEdges() const {
        std::vector<typename Graph::EdgePair> edges;

        for (size_t i = 0; i < nodes.size(); ++i) {
            for (const Edge &edge: nodes[i]->getNeighbors()) {
                edges.push_back(EdgePair(std::make_pair(i, edge.to->getId()), edge.data));
            }
        }

        return edges;
    }

    void DEBUG_PRINT(std::ostream &out = std::cout, bool isOneIndexed = true) const {
        for (const auto &node: nodes) {
            out << node->getId() + isOneIndexed << ": ";
            for (const auto &neighbor: node->getNeighbors()) {
                out << neighbor.to->getId() + isOneIndexed << " ";
            }
            out << std::endl;
        }
    }

    inline const std::vector<Node *> &getNodes() const {
        return nodes;
    }

    template<class Distance>
    std::vector<Distance> minPathDijkstraAll(const size_t &from,
                                             const std::function<Distance(const U &)> &getWeight) const {
        std::vector<Distance> distances(nodes.size(), std::numeric_limits<Distance>::max());
        distances[from] = 0;
        std::priority_queue<std::pair<Distance, Node *>, std::vector<std::pair<Distance, Node *>>, std::greater<>> pq;
        pq.push(std::make_pair(0, nodes[from]));
        while (!pq.empty()) {
            auto [distance, node] = pq.top();
            pq.pop();
            for (const auto &edge: node->getNeighbors()) {
                auto neighbor = edge.to;
                auto newDistance = distance + getWeight(edge.data);
                if (newDistance < distances[neighbor->getId()]) {
                    distances[neighbor->getId()] = newDistance;
                    pq.push(std::make_pair(newDistance, neighbor));
                }
            }
        }
        return distances;
    }

    template<class Distance>
    Distance minPathDijkstra(const size_t &from,
                             const size_t &to,
                             const std::function<Distance(const U &)> &getWeight) const {
        return minPathDijkstraAll(from, getWeight)[to];
    }

    void removeNodeConnections(const size_t& index){
        for(auto node = nodes[index]; auto neighbor : node->getNeighbors()){
            neighbor.to->removeNeighbor(node->getId());
        }
    }

    size_t connectedComponents(const std::function<bool &(Node *)> &getVisited) {
        size_t components = 0;
        bfs(getVisited,
            [&](const size_t& component, Node*){
                components = component + 1;
            },
            [&](const size_t&, Node*, Edge){});
        return components;
    }
};

template<class T = std::nullptr_t, class U = std::nullptr_t, bool storeParents = true>
using DirectedGraph = Graph<T, U, true, storeParents>;

#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <cstring>
#include <fstream>
#include <limits>

constexpr size_t MAX_VERTICES = 205;

bool bellmanFord(int start, int end, int vertices, std::vector<std::vector<int>>& weights, std::vector<std::vector<int>>& capacities, std::vector<int>& parents, std::vector<int>& distances, std::vector<std::vector<int>>& graphs) {
    std::fill(distances.begin(), distances.begin() + vertices, std::numeric_limits<int>::max());
    std::fill(parents.begin(), parents.begin() + vertices, -1);

    std::queue<int> q;
    distances[start] = 0;
    q.push(start);

    while (!q.empty()) {
        int currentVertex = q.front();
        q.pop();

        for (int neighbor : graphs[currentVertex]) {
            if (capacities[currentVertex][neighbor] > 0 && distances[neighbor] > distances[currentVertex] + weights[currentVertex][neighbor]) {
                distances[neighbor] = distances[currentVertex] + weights[currentVertex][neighbor];
                parents[neighbor] = currentVertex;
                q.push(neighbor);
            }
        }
    }

    return distances[end] != std::numeric_limits<int>::max();
}

int minCostMaxFlow(int start, int end, int vertices, std::vector<std::vector<int>>& weights, std::vector<std::vector<int>>& capacities, std::vector<int>& parents, std::vector<int>& distances, std::vector<std::vector<int>>& graphs) {
    int flow = 0, totalCost = 0;

    while (bellmanFord(start, end, vertices, weights, capacities, parents, distances, graphs)) {
        int pathFlow = std::numeric_limits<int>::max();
        for (int vertex = end; vertex != start; vertex = parents[vertex]) {
            int parentVertex = parents[vertex];
            pathFlow = std::min(pathFlow, capacities[parentVertex][vertex]);
        }

        for (int vertex = end; vertex != start; vertex = parents[vertex]) {
            int parentVertex = parents[vertex];
            capacities[parentVertex][vertex] -= pathFlow;
            capacities[vertex][parentVertex] += pathFlow;
            totalCost += pathFlow * weights[parentVertex][vertex];
        }

        flow += pathFlow;
    }

    return totalCost;
}

int main() {
    std::ifstream fin("cc.in");
    std::ofstream fout("cc.out");

    int totalNodes, maxFlow;
    std::vector<std::vector<int>> adjacencyMatrix(MAX_VERTICES, std::vector<int>(MAX_VERTICES, 0));
    std::vector<std::vector<int>> flowMatrix(MAX_VERTICES, std::vector<int>(MAX_VERTICES, 0));
    std::vector<std::vector<int>> capacityMatrix(MAX_VERTICES, std::vector<int>(MAX_VERTICES, 0));
    std::vector<std::pair<int, int>> nodeIndices;
    std::vector<int> parents(MAX_VERTICES, -1);
    std::vector<int> distances(MAX_VERTICES, 0);
    int sourceNode, targetNode = 101;

    fin >> totalNodes >> maxFlow;
    nodeIndices.resize(totalNodes + 2);
    adjacencyMatrix[100].push_back(targetNode);
    adjacencyMatrix[targetNode].push_back(100);
    capacityMatrix[100][targetNode] = maxFlow;
    int x, y, i;
    for (i = 1; i <= totalNodes; i ++) {
        fin >> x >> y;
        nodeIndices[i] = std::make_pair(x, y);
        adjacencyMatrix[x].push_back(y);
        adjacencyMatrix[y].push_back(x);
        capacityMatrix[x][y]++;
    }

    fout << minCostMaxFlow(sourceNode, targetNode, totalNodes, flowMatrix, capacityMatrix, parents, distances, adjacencyMatrix) << std::endl;

    return 0;
}