Cod sursa(job #2807999)

Utilizator waren4Marius Radu waren4 Data 24 noiembrie 2021 14:03:52
Problema Flux maxim Scor 70
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 50.23 kb
#include<iostream>
#include<fstream>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>

using namespace std;


// Auxiliary struct for holding edge information
struct Edge {                                       
        int source, destination, cost;          
        Edge(int source, int dest, int cost = 0): source(source), destination(dest), cost(cost){}
        // Returns edge with source and destination swapped
        Edge Flip() {return Edge(destination, source, cost);}   
};

// Class used for binary heap operations
template <typename T>
class Heap {  

public:
    
    // Returns maximum/minimum element
    T Root();
    // Returns maximum/minimum element and removes it from the heap
    T Extract();
    // Removes minimum/maximum element from the heap
    void Pop();
    // Inserts a element into the heap
    void Insert(T value);
    // Finds and removes an element by value from the heap
    // Does nothing if the element is not found
    int Remove(T value);
    // Inserts all elements of values into the heap
    void Build(vector<T> values);
    // Returns a vector of all elements in the heap and empties it
    vector<T> ExtractAll();

    // Returns true if heap is empty
    bool Empty() {
        return tree.empty();    
    }
    // Removes all elements from the heap
    void Clear() {
        tree.clear();
    }

    Heap(bool maxHeap = false) {
        this->maxHeap = maxHeap;
    }

    // Constructs a heap and inserts all elements of values into the heap
    Heap(vector<T> values, bool maxheap = false) {

        for(auto value : values) {
            Insert(value);
        }
    }

    // Returns the number of nodes currently in the heap
    int GetNumberOfNodes() {
        return tree.size();
    }

private:

    bool maxHeap;
    vector<T> tree;

    int Parent(int node);
    int LeftChild(int node);
    int RightChild(int node);
    void Descend(int node);
    void Ascend(int node);
    void Delete(int node);
    void SwapNodes(int node1, int node2);
    
    
    template<typename T0> static T0 Opposite(T0 x) {
        return -x;
    };
    static string Opposite(string x) {
        string newString = x;
        for(int i = 0; i < x.size(); ++i) {
            newString[i] = Opposite<char>(x[i]);
        }
        return newString;
    };
    template<typename T1, class T2> static pair<T1, T2> Opposite(pair<T1, T2> x)  {
        return make_pair(Opposite(x.first), x.second);
    };

};

#pragma region HeapPublicMethods

template<typename T>
T Heap<T>::Root() {  // Returns minimum/maximum element 

    if(maxHeap) {
        return Opposite(tree[0]);
    }
    else {
        return tree[0];
    }
}

template<typename T>
void Heap<T>::Insert(T value) { // Inserts element into heap

    T newValue = value;
    if(maxHeap) newValue = Opposite(value);

    tree.push_back(newValue);
    Ascend(tree.size() - 1);
}

template<typename T>
void Heap<T>::Pop() {  // Removes minimum/maximum element from the heap
    Delete(0);
}

template<typename T>
T Heap<T>::Extract() {  // Removes minimum/maximum element from the heap and returns it
    T root = Root();
    Delete(0);
    return root;
}

template<typename T>
int Heap<T>::Remove(T value) {   // Removes a given element from the heap, returns 0 if element was not found, returns 1 on succesful removal

    bool found = false;

    for(int i = 0; i < tree.size(); ++i) {
        if(tree[i] == value) {
            found = true;
            Delete(i);
            break;
        }
    }

    return found? 1 : 0;
}

template<typename T>
void Heap<T>::Build(vector<T> values) {   // Inserts all elements from values into the heap
    for(auto value : values) {
        Insert(value);
    }
}

template<typename T>
vector<T> Heap<T>::ExtractAll() {      // Returns a sorted vector of all elements

    vector<T> sortedVector;

    while(!Empty()) {
        sortedVector.push_back(Extract());
    }

    return sortedVector;
}

#pragma endregion

#pragma region HeapPrivateMethods

template<typename T>
void Heap<T>::Descend(int node) {

    T minimum = tree[node];
    int left = LeftChild(node);
    int right = RightChild(node);
    int minIndex = node;

    if(left) {
        if(minimum > tree[left]) {
            minIndex = left;
            minimum = tree[left];
        }
    }

    if(right) {
        if(minimum > tree[right]) {
            minIndex = right;
        }
    }

    if(node != minIndex) {
        SwapNodes(node, minIndex);
        Descend(minIndex);
    }
}    

template<typename T>
void Heap<T>::Ascend(int node) {

    int parent = Parent(node);

    if(tree[parent] > tree[node]) {
        SwapNodes(node, parent);
        Ascend(parent);
    }
}

template<typename T>
void Heap<T>::Delete(int node) {
    
    SwapNodes(node, tree.size()-1);
    tree.pop_back();
    Descend(node);
}

template<typename T>
void Heap<T>::SwapNodes(int node1, int node2) {

    T swap = tree[node1];
    tree[node1] = tree[node2];
    tree[node2] = swap;
}

template<typename T>
int Heap<T>::Parent(int node) {

    if(node > 0) {
        return (node - 1) / 2;
    }
    else return 0;
}

template<typename T>
int Heap<T>::LeftChild(int node) {
    int left = node * 2 + 1;

    if( left < tree.size()) {
        return left;
    }
    else return 0;
}

template<typename T>
int Heap<T>::RightChild(int node) {
    int right = node * 2 + 2;

    if( right < tree.size()) {
        return right;
    }
    else return 0;
}

#pragma endregion

// Struct used as helper for creating and manipulating disjoint sets of nodes
struct DisjointSets {    

    // Returns the root of the node's component
    int Root(int node);
    // Merges the components of node1 and node2
    // The smaller component's root is always attached to the larger one's root
    void Union(int node1, int node2);

    // Constructs and initializes numberOfNodes nodes into numberOfNodes disjoint sets of rank 1
    DisjointSets(int numberOfNodes): numberOfNodes(numberOfNodes) {

        for(int node = 0; node < numberOfNodes; ++node) {
            rank.push_back(1);
            parent.push_back(node);
        }
    }

    private:

        int numberOfNodes;

        vector<int> rank;           // Holds the rank of the set of each node, for optimizing unions
        vector<int> parent;         // Either the direct parent's index or the root node index 

};

#pragma region DisjointSetsPublicMethods

int DisjointSets::Root(int node) {        // Returns the root of the node's subtree

    int root = node;

    while(parent[root] != root) {
        root = parent[root];        // Go up through the set until its root is found
    }


    while(node != parent[node]) {   // Update parent[node] to the set's root, for all nodes up to the root
        int swap = parent[node];
        parent[node] = root;
        node = swap; 
    }

    return root;
}

void DisjointSets::Union(int node1, int node2) {  // Merges the two subtrees by connecting one's root to the other's
    node1 = Root(node1);            // Get the roots 
    node2 = Root(node2);

    if (rank[node1] > rank[node2]) {    // Link roots, smaller rank gets attached to higher rank 
        parent[node2] = node1;
    }
    else {
        parent[node1] = node2;
    }
    if(rank[node1] == rank[node2]) {
        ++rank[node2];              // Increase rank of new set if sets were equal 
    }
}

#pragma endregion

class FlowHandler;

class Graph {

public:

        // Holds the max value for int, used to represent infinite distances(for unreachable nodes)
        // = 2147483647 
        static const int INFINITE; 
        // Returns the sum of all edge weights
        int TotalCost();                                        
        // Returns the number of connected components (for undirected graphs)
        int NumberOfComponents();        
        // Returns the diameter of the graph(length of longest sequence of connected nodes). Should only be used on trees
        int TreeDiameter();              
        // Returns the maximum flow from source to sink. Should only be used after BuildFlowNetwork() has been called
        // Requires handler to be the FlowHandler that was passed to BuildFlowNetwork()
        int MaximumFlow(unsigned int source, unsigned int sink, FlowHandler& handler);         
        // Returns all edges in the graph
        vector<Edge> GetAllEdges();                             
        // Returns a vector of distances from node of index startIndex to all others, ignoring weights
        vector<int> UnweightedDistances(int startIndex = 0);    
        // Returns a vector of distances from node of index startIndex to all others 
        // Uses Dijkstra's algorithm if there are no negative edges, otherwise uses the Bellman-Ford algorithm
        vector<int> WeightedDistances(int startIndex = 0);      
        // Returns an adjacency matrix, returnMatrix[i][j] represents the cost of the minimum path from node i to node j
        // returnMatrix[i][j] == Graph::INFINITE means there is no edge from i to j
        vector<vector <int>> AllMinimumDistances();
        // Returns a vector containing the topological sort of current graph (for directed graphs)
        vector<int> TopologicalSort();                          
        // Returns vectors of node indexes, grouped into the graph's strongly connected components (for directed graphs)
        vector<vector <int>> StronglyConnectedComponents();     
        // Returns vectors of node indexes, grouped into the graph's biconnected components 
        vector<vector <int>> BiconnectedComponents();  
        // Returns a vector of the critical edges in the graph
        vector<Edge> CriticalConnections();                     

        // Returns a new graph that is the MST of this graph
        Graph MinimumSpanningTree();
        // Returns a new graph that is the DF search tree of this graph starting in startIndex
        Graph DFSTree(int startIndex);
        // Returns a new graph that is the BF search tree of this graph starting in startIndex
        Graph BFSTree(int startIndex);
        // Returns a new graph that contains all DFS trees of this graph
        // Starts a DFS from index 0, then visits all unvisited nodes, adding the other trees to the returned graph
        Graph DFSTrees();
        
        // Reads edges from inputStream, formated as adjacency list entries: nodeX, nodeY, capacity, cost
        // and builds a flow network graph using handler as a helper for operations on the network
        // Should only be used after setting numberOfNodes and numberOfEdges 
        void BuildFlowNetwork(istream& inputStream, FlowHandler& handler);

        // Reads edges from inputStream, formated as adjacency list entries: nodeX, nodeY, cost
        // Should only be used if numberOfEdges has already been set
        void BuildFromAdjacencyList(istream& inputStream);
        
        // Reads edges from inputStream, formated as an adjacency matrix: 
        // 0 represents no edge, any other value represents the cost of the edge
        // If upon calling numberOfEdges == 0 (has not been set), sets numberOfEdges according to the matrix
        void BuildFromAdjacencyMatrix(istream& inputStream);

        // Returns an adjacency matrix for the graph, returnMatrix[i][j] represents the cost from node i to node j 
        // returnMatrix[i][j] == Graph::INFINITE means there is no edge from i to j
        vector<vector<int>> GetAdjacencyMatrix();
        // Adds a new edge to the graph, increases numberOfEdges
        void AddEdge(int source, int dest, int cost = 0);
        // Adds a new edge to the graph, increases numberOfEdges
        void AddEdge(Edge newEdge);
        // Adds a new node to the graph, increases numberOfNodes
        void AddNode(int value = 0);

        // Takes a vector of node degree values and checks if a graph can be formed using the Havel-Hakimi algorithm 
        static bool CheckHavelHakimi(vector<int> degrees);

        

#pragma region GraphConstructors

        Graph(bool directed = false, bool weighted = false) {

            numberOfNodes = 0;
            numberOfEdges = 0;
            this->directed = directed;
            this->weighted = weighted; 
        }

        Graph(int numberOfNodes, int numberOfEdges = 0, bool directed = false, bool weighted = false) {
            
            this->numberOfEdges = numberOfEdges;
            this->directed = directed;
            this->weighted = weighted; 

            for(int i = 0; i < numberOfNodes; ++i) {
                
                AddNode();
            }

        }

#pragma endregion

#pragma region GraphGetSet

        void SetValue(int node, int value) {

            try {
                values[node] = value;
            }
            catch(...) {
                throw "Node doesn't exist";
            }
        }

        int GetValue(int node) const {
            
            try {
                return values[node];
            }
            catch(...) {
                throw "Node doesn't exist";
            }
            
        }

        void SetDirected(bool directed) {
            this->directed = directed;
        }

        bool IsDirected() const {
            return directed;
        }

        void SetWeighted(bool weighted) {
            this->weighted = weighted;
        }

        bool IsWeighted() const {
            return weighted;
        }

        void SetNumberOfNodes(int number) {
            numberOfNodes = number;
        }
        
        int GetNumberOfNodes() const {
            return numberOfNodes;
        }

        void SetNumberOfEdges(int number) {
            numberOfEdges = number;
        }

        int GetNumberOfEdges() const {
            return numberOfEdges;
        }

#pragma endregion

    private:

        bool directed;
        bool weighted;

        int numberOfNodes;
        int numberOfEdges;

        vector < int > values;                              // The value of each node 
        vector < vector < Edge > > adjacencyList;           // adjacencyList[X] holds the information of each edge that X has

        
        void Dijkstra(vector<bool>& visitedNodes, vector<int>& distances, Heap< pair<int, int>>& heap, int startIndex = 0);        
        void BellmanFord(vector<bool>& betterPath, vector<int>& distances, Heap<pair<int, int>>& heap, int startIndex = 0);
        void BFS(vector<bool>& visitedNodes, vector<int>& distances, int startIndex = 0);
        void FlowBFS(vector<bool>& visitedNodes, vector<int>& prev, FlowHandler& fh, int startIndex, int sink);
        void TreeBuilderBFS(int startIndex, Graph& treeGraph, vector<bool>& visitedNodes);
        void DFS(vector<int>& visitedNodes, int marker = 1, int nodeIndex = 0);
        void StronglyConnectedDFS(int currentNode, vector<vector<int>>& scc, vector<int>& order, vector<int>& lowest, stack<int>& nodeStack, vector<bool>& onStack, int& counter);
        void BiconnectedDFS(int currentNode, int parent, int currentDepth, vector< vector <int>>& bcc, vector<int>& depth, vector<int>& lowest, stack<int>& nodeStack, vector<bool>& visited);
        void CriticalEdgesDFS(int currentNode, int parent, vector <Edge>& cc, vector<int>& depth, vector<int>& lowest, int& counter);
        void TopologicalDFS(int currentNode, stack<int>& orderStack, vector<bool>& visitedNodes);
        void TreeBuilderDFS(int currentNode, int treeCurrentNode, Graph& treeGraph, vector<bool>& visitedNodes);

        bool HasNegativeEdges();
        static bool CompareEdges(Edge x, Edge y);
    
};
const int Graph::INFINITE = 2147483647;

// Class used as a helper for graph class, to be passed as parameter to graph methods that operate on flow networks 
// Should be instantianted after initializing graph with numberOfNodes and numberOfEdges
class FlowHandler {

    friend class Graph;

public:

    // The matrix for storing capacity of each edge. capacityMatrix[x][y] holds the capacity of the edge x -> y
    vector< vector< int >> capacityMatrix;

    // Must receive a pointer to the graph that represents the flow network and the indexes of the source and sink nodes
    FlowHandler(Graph* graphPointer, int sourceIndex, int sinkIndex): graph(graphPointer), source(sourceIndex), sink(sinkIndex) {
        
        numberOfNodes = graph->GetNumberOfNodes();

        for(int i = 0; i < numberOfNodes; ++i) {
        
            vector<int> tempVector;
            capacityMatrix.push_back(tempVector);
            flowMatrix.push_back(tempVector);
            for(int j = 0; j < numberOfNodes; ++j) {
                capacityMatrix[i].push_back(0);
                flowMatrix[i].push_back(0);
            }
        }
    }
    
    int GetSource() {return source;}
    int GetSink() {return sink;}

    void SetSource(unsigned int source) { 
        try {
            if (source < numberOfNodes) this->source = source;
            else throw;
        }
        catch (...) {
            throw "Source index out of range";
        }
    }

    void SetSink(unsigned int sink) {
        try {
            if (sink < numberOfNodes) this->sink = sink;
            else throw;
        }
        catch (...) {
            throw "Sink index out of range";
        }
    }

private:
    // The graph this FlowHandler is corelated to
    const Graph* graph; 
    int numberOfNodes;
    int source, sink;

    vector< vector< int >> flowMatrix;
};

#pragma region GraphPublicMethods

void Graph::AddEdge(int source, int dest, int cost /*= 0*/) {

    Edge newEdge(source, dest, cost);

    adjacencyList[source].push_back(newEdge);

    if(!this->directed) {

        adjacencyList[dest].push_back(newEdge.Flip());
    }

    ++numberOfEdges;
}

void Graph::AddEdge(Edge newEdge) {

    adjacencyList[newEdge.source].push_back(newEdge);

    if(!this->directed) {

        adjacencyList[newEdge.destination].push_back(newEdge.Flip());
    }

    ++numberOfEdges;
}

void Graph::AddNode(int value /*= 0*/) {

    vector<Edge> tempVector;
    adjacencyList.push_back(tempVector);

    values.push_back(value);

    ++numberOfNodes;
}

vector<Edge> Graph::GetAllEdges() {

    vector<Edge> edges;

    if (directed) {
        for(int node = 0; node < numberOfNodes; ++node) {
            for(auto edge : adjacencyList[node]) {
                edges.push_back(edge);            
            }
        }
    }
    else {
    
        vector<bool> visited;                   // Used for not returning duplicate edges in undirected graphs

        for(int i = 0; i < numberOfNodes; ++i) {
            visited.push_back(false);
        }

        for(int node = 0; node < numberOfNodes; ++node) {
            for(auto edge : adjacencyList[node]) {
                if (!visited[edge.destination]) {
                    edges.push_back(edge);
                }
            }
        
        visited[node] = true;

        }
    }

    return edges;
}

Graph Graph::MinimumSpanningTree() {
                                                // Returns a graph that is a minimum spanning tree of this graph, using Kruskal's algorithm and disjoint sets

    Graph treeGraph(numberOfNodes, 0, directed, weighted);  // Initialize graph to be returned
                                                            // Has 0 edges currently because numberOfEdges will be incremented by AddEdge()
    vector<Edge> sortedEdges = GetAllEdges();
    sort(sortedEdges.begin(), sortedEdges.end(), CompareEdges);

    DisjointSets sets(numberOfNodes);           // Used for keeping track of the new trees components

    for(int i = 0; i < numberOfEdges; ++i) {

        if( sets.Root(sortedEdges[i].source) != sets.Root(sortedEdges[i].destination) )  {       // Check if nodes are in separate components

            sets.Union(sortedEdges[i].source, sortedEdges[i].destination);
            treeGraph.AddEdge(sortedEdges[i]);
        }
    }

    return treeGraph;
}

int Graph::TotalCost() {
                            // Returns the summed cost of all edges in graph
    int totalCost = 0;

    vector<Edge> edges = GetAllEdges();

    for(auto edge : edges) {
        
        totalCost += edge.cost;
    }

    return totalCost;
}

int Graph::TreeDiameter() {

    vector<int> distances = UnweightedDistances(); // Perform a BFS

    int maximum = 0;
    int maxIndex;

    for(int i = 0; i < numberOfNodes; ++i) {
        if (distances[i] > maximum) {
            maximum = distances[i];
            maxIndex = i;
        }
    }

    distances = UnweightedDistances(maxIndex);  // Perform a second BFS from the node that's farthest away
    maximum = 0;

    for(int i = 0; i < numberOfNodes; ++i) {
        if(distances[i] > maximum) {
            maximum = distances[i];
        }
    }

    return maximum + 1; // Add 1 to count starting node 
}

int Graph::MaximumFlow(unsigned int source, unsigned int sink, FlowHandler& handler) {

    int totalFlow = 0;

    vector<bool> visited; // For marking visited nodes
    vector<int> previous; // Used for tracking paths to sink 

    for(int i = 0; i < numberOfNodes; ++i) {    // Initialization
        visited.push_back(false);
        previous.push_back(0);
    }
    
    FlowBFS(visited, previous, handler, source, sink);

    while(visited[sink]) {

        for(auto &edge : adjacencyList[sink]) {  // By checking the paths of all nodes that connect to the sink, we can reduce the number of BFS's that must be performed

            previous[sink] = edge.destination;
            int minimumIncrease = Graph::INFINITE;
            int node = sink; // Current node in path
            
            while(node != source) { // Go back through the path to find the minimum possible increase for the flow of the whole path
                int prev = previous[node];
                int increase = handler.capacityMatrix[prev][node] - handler.flowMatrix[prev][node];

                if(minimumIncrease > increase) {
                    minimumIncrease = increase;
                }
                
                node = prev;
            }

            
            if(minimumIncrease != 0) { // If an improvement was found 
                node = sink;
                while(node != source) { // Go back through the path to find the minimum possible increase for the flow of the whole path
                    int prev = previous[node];
                    
                    handler.flowMatrix[prev][node] += minimumIncrease;
                    handler.flowMatrix[node][prev] -= minimumIncrease; // Set residual graph edge 

                    node = prev;
                }

                totalFlow += minimumIncrease;
            }

        }

        for(int i = 0; i < numberOfNodes; ++i) {
            visited[i] = false;
        }
        FlowBFS(visited, previous, handler, source, sink);
    }

    return totalFlow;
}

Graph Graph::DFSTree(int startIndex) {
                                            // Returns a graph object of the DFS tree of node with startIndex index
                                            // Node of index startIndex will be the node of index 0 in the new graph
    Graph treeGraph(1, 0, directed, weighted);        // Initialize empty graph with one node
    vector<bool> visitedNodes;

    for(int i = 0; i < numberOfNodes; ++i) {

        visitedNodes.push_back(false);
    }

    TreeBuilderDFS(startIndex, 0, treeGraph, visitedNodes);
    
    return treeGraph;
}

Graph Graph::DFSTrees() {
                                            // Returns a graph object of the DFS tree of node with startIndex index
                                            // Node of index startIndex will be the node of index 0 in the new graph
    Graph treeGraph(0, 0, directed, weighted);        // Initialize empty graph
    vector<bool> visitedNodes;

    for(int i = 0; i < numberOfNodes; ++i) {

        visitedNodes.push_back(false);
    }

    for(int node = 0; node < numberOfNodes; ++node) {
        
        if(!visitedNodes[node]) {

            treeGraph.AddNode();
            TreeBuilderDFS(node, treeGraph.numberOfNodes-1, treeGraph, visitedNodes);
        }
    }

    return treeGraph;
}

Graph Graph::BFSTree(int startIndex) {

    Graph treeGraph(0, 0, directed, weighted);
    vector<bool> visitedNodes;

    for(int i = 0; i < numberOfNodes; ++i) {

        visitedNodes.push_back(false);
    }

    TreeBuilderBFS(startIndex, treeGraph, visitedNodes);

    return treeGraph;
}

vector<int> Graph::TopologicalSort() {
                                            // Returns the list of nodes in topologically sorted order
                                            // Should only be applied on directed graphs
    vector<int> sortedNodes;

    stack<int> orderStack;                  // By using a stack, the elements in pop order will form the topologically sorted list
    vector<int> depth;
    vector<bool> visited;

    for(int i = 0; i < numberOfNodes; ++i) {

        visited.push_back(false);    
    }

    for(int i = 0; i < numberOfNodes; ++i) {

        if(!visited[i]) { 
            TopologicalDFS(i, orderStack, visited);
        }
    }

    for(int i = 0; i < numberOfNodes; ++i) {

        sortedNodes.push_back(orderStack.top());
        orderStack.pop();
    }

    return sortedNodes;

}

bool Graph::CheckHavelHakimi(vector<int> degrees) {
                                                                            // Receives a list of node degrees and returns true if a corelated graph can exist through the Havel-Hakimi algorithm
   
    int maximum = -1; 
    int sum = 0;  
    int numberOfNodes = degrees.size();

    for(int degree : degrees) {

        if(maximum < degree) {
            maximum = degree;
        }
        sum += degree;
        if (degree >= numberOfNodes) {
                                    // If degree is > n-1 then there are not enough nodes, therefore a graph doesn't exist
            return false; 
        }
    }

    if(sum % 2) {                           // If sum of degrees is odd then a graph doesn't exist
        return false; 
    }

    vector<int> frequency(maximum + 1, 0);
    
    for(int degree : degrees) {
        ++frequency[degree];    // Form frequency vector
    }

    stack<pair<int, int>> changeStack;  // Stack used to keep track of changes on the frequency vector
                                        // .first is the node whose frequency number has to have .second added to it
    while(sum > 0) {
        pair <int, int> stackTop;
        
        while(!changeStack.empty()) {
            stackTop = changeStack.top();
            frequency[stackTop.first] += stackTop.second;
            changeStack.pop();

            if(stackTop.first > maximum) maximum = stackTop.first;
        }

        while(frequency[maximum] == 0) {    // Find first value still in vector
            --maximum;
            if(maximum < 0) return false;       // There are no more numbers, so there are too many subtractions, therefore no graph exists
        }

        int subtract = maximum; // Value to be subtracted from vector
        int oldMax = maximum;
        --frequency[maximum];
        
        while(subtract > 0) {

            while(frequency[maximum] == 0) {    // Find first value still in vector
                --maximum;
                if(maximum < 0) return false;       // There are no more numbers, so there are too many subtractions, therefore no graph exists
            }

            if(frequency[maximum] >= subtract) {
                if(maximum > 1) changeStack.push(make_pair(maximum - 1, subtract));
                frequency[maximum] -= subtract;
                subtract = 0;
            }
            else {
                if(maximum > 1) changeStack.push(make_pair(maximum - 1, frequency[maximum])); 
                subtract -= frequency[maximum];
                frequency[maximum] = 0;
            }

        }
        sum -= 2*oldMax;
    }
    return true;            // If this point is reached then the vector is now [0, 0, .. 0] and a graph exists for the given set of degrees          
}

vector<Edge> Graph::CriticalConnections() {
                                                         // Tested on leetcode(Critical Connections problem)  
    vector<Edge> cc;                    // The list of critical edges

    vector<int> order;                  // Node X is the order[x]-th node to be found during DFS
    vector<int> lowest;                 // lowest[X] is the minimum order[Y], where node Y is connected to node X
     
    int counter = 0;

    for(int i = 0; i < numberOfNodes; ++i) {

        order.push_back(-1);            // -1 order means node hasn't been visited yet
        lowest.push_back(-1);
    }

    CriticalEdgesDFS(0, -1, cc, order, lowest, counter);  // Root node has no parent

    return cc;
} 

vector<int> Graph::UnweightedDistances(int startIndex /*= 0*/) {
                                                                        // Wrapper Method for calculating unweighted distances to each node form startIndex through BFS
                                                                        // Saves distances in distances parameter(distances vector should be empty when calling this method)
    
    vector<bool> visited;
    vector<int> distances;

    
    for(int i = 0; i < numberOfNodes; ++i) {
        visited.push_back(0);
        distances.push_back(-1);                // -1 means there is no path to a certain node
    }

    BFS(visited, distances, startIndex);

    return distances;
}

vector<int> Graph::WeightedDistances(int startIndex /*= 0*/) {
                                                // Computes the weighted distances from node of index startIndex to all other nodes
                                                // Uses BellmanFord instead of Dijkstra if there are negative weight edges
                                                // Return vector has distances[startIndex] == 1 if there are negative weight cycles
    vector<int> distances;
    vector<bool> visited;
    Heap< pair<int, int> > heap; 
    // The heap will contain pairs of (distance, node), where distance is the current total distance to node
        
    int infinity = 2147483647;      // Used for initializing distance vector

    for(int i = 0; i < numberOfNodes; ++i) {

        if(i == startIndex) {
            distances.push_back(0);     
        }
        else {
            distances.push_back(infinity);
        }

        visited.push_back(false);
    }

    if(HasNegativeEdges()) {
        BellmanFord(visited, distances, heap, startIndex);
    }   
    else { 
        Dijkstra(visited, distances, heap, startIndex);
    }
    return distances;
}

vector<vector <int>> Graph::AllMinimumDistances() {

    vector<vector<int>> adjMatrix = GetAdjacencyMatrix();

    for(int node = 0; node < numberOfNodes; ++node) {    // For each node, check if it can be used to make any path shorter
        for(int i = 0; i < numberOfNodes; ++i) {
            for(int j = 0; j < numberOfNodes; ++j) {
                
                if(i == j) continue;
                if(adjMatrix[i][node] != INFINITE && adjMatrix[node][j] != INFINITE ) {  // If a path from i to j through node exists

                    int detourPath = adjMatrix[i][node] + adjMatrix[node][j];
                    if(adjMatrix[i][j] > detourPath || adjMatrix[i][j] == INFINITE ) {
                        adjMatrix[i][j] = detourPath;
                    }
                }
            }
        }
    }

    return adjMatrix;

}

int Graph::NumberOfComponents() {
                                        // Computes number of components in undirected graph 
    int numberOfComponents = 0;

    vector<int> visited;
    for(int i = 0; i < numberOfNodes; ++i) {
        visited.push_back(0);
    }

    for(int i = 0; i < numberOfNodes; ++i) {

        if (!visited[i]) {
            ++numberOfComponents;
            DFS(visited, numberOfComponents, i);
        }
    }

    return numberOfComponents;
}

vector< vector <int> > Graph::BiconnectedComponents() {
                                                                    // Returns the list of biconnected components in graph

    vector< vector<int> > bcc;      // The list of BCCs to be returned

    stack<int> nodeStack;           // The stack of visited nodes
    
    vector<int> depth;              // Holds the depth of each node in the DFS tree
    vector<int> lowest;             // Holds the lowest depth a node can reach through its descendants
    vector<bool> visited;           // For marking visited nodes

    for(int i = 0; i < numberOfNodes; ++i) {
        depth.push_back(-1);        // Initialization
        lowest.push_back(-1);   
        visited.push_back(false);
    }

    BiconnectedDFS(0, -1, 0, bcc, depth, lowest, nodeStack, visited);       // Root node (0) has no parent

    return bcc;
}

vector< vector <int> > Graph::StronglyConnectedComponents() {
                                                                    // Computes the number of strongly connected components in directed graph through Tarjan's algorithm
    vector< vector <int> > scc;         // The list of strongly connected components to be returned

    stack<int> nodeStack;               // Stack to be used in tarjan's algorithm

    vector<int> order;                  // Node X is the order[x]-th node to be found during DFS
    vector<int> lowest;                 // lowest[X] is the minimum order[Y], where node Y is connected to node X
    vector<bool> onStack;               // onStack[X] is true if node X is currently on the stack 
    
    int counter = 0;                    // Counter for the order vector

    for(int i = 0; i < numberOfNodes; ++i) {
        order.push_back(-1);            // -1 means node hasn't been visited
        lowest.push_back(-1);           
        onStack.push_back(false);
    }


    for(int node = 0; node < numberOfNodes; ++node) {

        if (order[node] < 0) {

            StronglyConnectedDFS(node, scc, order, lowest, nodeStack, onStack, counter);
        }
    }

    return scc;
}

vector<vector<int>> Graph::GetAdjacencyMatrix() {

    vector<vector<int>> matrix;

    for(int i = 0; i < numberOfNodes; ++i) {
        vector<int> tempVector(numberOfNodes, INFINITE);
        matrix.push_back(tempVector);
    }

    for(int i = 0; i < numberOfNodes; ++i) {
        for(auto edge : adjacencyList[i]) {

            matrix[i][edge.destination] = edge.cost;
        }
    }

    return matrix;
}

void Graph::BuildFromAdjacencyMatrix(istream& inputStream) {        // Sets edges between nodes by reading an adjacency matrix from inputStream
                                                                    // Should only be used if numberOfEdges has already been set 

    bool incrementNumber = false;
    if(!numberOfEdges) {
        incrementNumber = true;
    }

    int matrixValue;             // If matrixValue is 0, there is no edge, if matrixValue != 0, then there is an edge of cost matrixValue

    for(int i = 0; i < numberOfNodes; ++i) {
        for (int j = 0; j < numberOfNodes; ++j) {

            inputStream >> matrixValue;

            if(matrixValue) {
                
                if(!weighted) {
                    matrixValue = 0;
                }

                Edge newEdge(i, j, matrixValue);

                if(incrementNumber) {
                    AddEdge(newEdge);
                    continue;
                }
                
                adjacencyList[i].push_back( newEdge );

                if(!directed) {    
                    adjacencyList[j].push_back( newEdge.Flip() );
                }
               
            }
        } 
    }
}

void Graph::BuildFromAdjacencyList(istream& inputStream) {          // Sets edges between nodes by reading adjancency list pairs from inputStream
                                                                    // Should only be used if numberOfEdges has already been set 
                                                                    
    int node1, node2, cost;

    for(int i = 0; i < numberOfEdges; ++i) {

        inputStream >> node1 >> node2;

        if(weighted) {
            inputStream >> cost;
        }
        else {
            cost = 0;
        }
        
        Edge newEdge(node1, node2, cost);

        adjacencyList[node1].push_back( newEdge );
      
        if(!directed) {
                adjacencyList[node2].push_back( newEdge.Flip() );
        }
    }   
}

void Graph::BuildFlowNetwork(istream& inputStream, FlowHandler& handler) {

    int node1, node2, capacity, cost;

    for(int i = 0; i < numberOfEdges; ++i) {

        inputStream >> node1 >> node2 >> capacity;
        --node1; --node2; // deoarece pe infoarena nodurile sunt indexate de la 1

        if(weighted) {
            inputStream >> cost;
        }
        else {
            cost = 0;
        }
        
        Edge newEdge(node1, node2, cost);
        adjacencyList[node1].push_back( newEdge );
        adjacencyList[node2].push_back( newEdge.Flip() );

        handler.capacityMatrix[node1][node2] = capacity;

    }
}

#pragma endregion

#pragma region GraphPrivateMethods

bool Graph::HasNegativeEdges() {

    vector<Edge> edges = GetAllEdges();
    for(auto edge : edges) {
        if ( edge.cost < 0 ) return true;
    }
    return false;
}

bool Graph::CompareEdges(Edge x, Edge y) {
    return (x.cost < y.cost); 
}

void Graph::Dijkstra(vector<bool>& visitedNodes, vector<int>& distances, Heap < pair<int, int> >& heap, int startIndex /*= 0*/) {

    heap.Insert(make_pair(0, startIndex));    // Push starting node

    while(!heap.Empty()) {

        int currentDistance = heap.Root().first;
        int currentNode = heap.Root().second;

        visitedNodes[currentNode] = true;
        heap.Pop();

        if(distances[currentNode] < currentDistance) {  // If the current minimum distance is smaller than the one in the current pair, 
                                                        // then there is no point in processing the pair further, 
            continue;                                   // because it can't lead to shorter paths
        }

        for(auto edge : adjacencyList[currentNode]) {

            if(!visitedNodes[edge.destination]) {       // If we have already processed a node, then the minimum distance to it has already been calculated

                if(distances[edge.destination] > currentDistance + edge.cost) {
                    
                    distances[edge.destination] = currentDistance + edge.cost;
                    heap.Insert(make_pair(distances[edge.destination], edge.destination));
                }
            }
        }
    }
}   

void Graph::BellmanFord(vector<bool>& betterPath, vector<int>& distances, Heap<pair<int, int>>& heap, int startIndex /*= 0*/) {

    heap.Insert(make_pair(0, startIndex));

    long long loopCounter = 0;  // Used to count the number of loops in while cycle
                                // If loopCounter goes above (E-1)*V, 
                                // then there must be a negative weight cycle in the graph
    while(!heap.Empty()) {

        if(loopCounter > 1LL* numberOfEdges * (numberOfNodes - 1)) { // Too many improvements, there is a negative weight cycle
            distances[startIndex] = 1; // Marker for negative weight cycle
            break;
        }
        
        int currentNode = heap.Extract().second;
        betterPath[currentNode] = false;

        for(auto edge : adjacencyList[currentNode]) {

            if(distances[edge.destination] > distances[currentNode] + edge.cost) {  // Found a shorter path

                distances[edge.destination] = distances[currentNode] + edge.cost;

                if(!betterPath[edge.destination]) { // Found a new relevant node, so insert it into the heap
                    
                    betterPath[edge.destination] = true;
                    heap.Insert(make_pair(distances[edge.destination], edge.destination));
                }
            }
        }

        ++loopCounter;
    }
}

void Graph::TreeBuilderBFS(int startIndex, Graph& treeGraph, vector<bool>& visitedNodes) {
                                                                        // BFS that also builds a new graph(the BFS tree)
    queue<int> nodeQueue;
    treeGraph.AddNode();
    int treeCurrentNode = 0;        // Index of the current node in the new graph

    nodeQueue.push(startIndex);

    while(!nodeQueue.empty()) {

        int topNode = nodeQueue.front();

        visitedNodes[topNode] = true;

        for (auto edge : adjacencyList[topNode]) {

            int neighbor = edge.destination;
             
            if(!visitedNodes[neighbor]) {

                treeGraph.AddNode();
                treeGraph.AddEdge(treeCurrentNode, treeGraph.numberOfNodes-1);

                nodeQueue.push(neighbor);
                visitedNodes[neighbor] = true;
            }
        }

        treeCurrentNode += 1;       // The next node the search will process has this index in the tree
        nodeQueue.pop();
    }

}

void Graph::TreeBuilderDFS(int currentNode, int treeCurrentNode, Graph& treeGraph, vector<bool>& visitedNodes) {
                                                                        // DFS that also builds a new graph(the DFS tree)
    visitedNodes[currentNode] = true;

    for(auto edge : adjacencyList[currentNode]) {

        int child = edge.destination;

        if(!visitedNodes[child]) {

            treeGraph.AddNode();
            treeGraph.AddEdge(treeCurrentNode, treeGraph.numberOfNodes-1);

            TreeBuilderDFS(child, treeGraph.numberOfNodes-1, treeGraph, visitedNodes);
        }
    }
}

void Graph::BFS(vector<bool>& visitedNodes, vector<int>& distances, int startIndex /*= 0*/) {
                                                                        // Breadth-first search that sets visited nodes and distance to each node from node with index startIndex
    queue<int> nodeQueue;           // Queue of nodes that haven't had their edges processed yet

    nodeQueue.push(startIndex);
    distances[startIndex] = 0;      // Distance to starting node

    while(!nodeQueue.empty()) {

        int topNode = nodeQueue.front();

        visitedNodes[topNode] = 1;

        for(auto edge : adjacencyList[topNode]) {

            int neighbor = edge.destination;

            if(!visitedNodes[neighbor]) {
                
                nodeQueue.push(neighbor);
                distances[neighbor] = 1 + distances[topNode];
                visitedNodes[neighbor] = 1;
            }
        }

        nodeQueue.pop();
    }

}

void Graph::FlowBFS(vector<bool>& visitedNodes, vector<int>& prev, FlowHandler& fh, int source, int sink) {

    // Performs BF searches for Edmonds-Karp algorithm, setting the previous node for each node it reaches
    queue<int> nodeQueue;
    nodeQueue.push(source);

    while(!nodeQueue.empty()) {

        int currentNode = nodeQueue.front();
        nodeQueue.pop();

        visitedNodes[currentNode] = true;

        if(currentNode != sink) {

            for(auto& edge: adjacencyList[currentNode]) {
                int neighbor = edge.destination;

                if(!visitedNodes[neighbor] && fh.flowMatrix[currentNode][neighbor] < fh.capacityMatrix[currentNode][neighbor]) { // If more flow can fit
                    
                    prev[neighbor] = currentNode;
                    nodeQueue.push(neighbor);
                }
            }
        }
    }
}

void Graph::DFS(vector<int>& visitedNodes, int marker /*= 1*/, int nodeIndex /*= 0*/) {
                                                                                        // Recursive depth-first search, sets visited positions in visitedNodes with marker for counting components
    visitedNodes[nodeIndex] = marker;

    for(auto edge : adjacencyList[nodeIndex]) {

        int neighbor = edge.destination;

        if(!visitedNodes[neighbor]) {
            DFS(visitedNodes, marker, neighbor);
        }
    }

}

void Graph::StronglyConnectedDFS(int currentNode, vector<vector<int>>& scc, vector<int>& order, vector<int>& lowest, stack<int>& nodeStack, vector<bool>& onStack, int& counter) {

    order[currentNode] = counter;
    lowest[currentNode] = counter;

    ++counter;

    nodeStack.push(currentNode);
    onStack[currentNode] = true;


    for(auto edge : adjacencyList[currentNode]) {

        int neighbor = edge.destination;

        if(order[neighbor] < 0) {           // If node hasn't been visited yet

            StronglyConnectedDFS(neighbor, scc, order, lowest, nodeStack, onStack, counter);

            lowest[currentNode] = min(lowest[currentNode], lowest[neighbor]);               // Set the SCC index of each node on the recursion return path
        }
        else {
            if (onStack[neighbor]) {
                                                                                            // If neighbor isn't on the stack, then neighbor is part of a different, previously discovered SCC
                lowest[currentNode] = min(lowest[currentNode], order[neighbor]);
            }
        }
    }

    if(lowest[currentNode] == order[currentNode]) {                                         // If lowest[X] = order[X] then X has no back-edge and is the root of its SCC
                                                                                            // The stack must be popped up to said root, as we have found a complete SCC                                                                                      
        vector<int> currentScc;

        int stackTop;

        do {

            stackTop = nodeStack.top();
            
            nodeStack.pop();
            onStack[stackTop] = false;

            currentScc.push_back(stackTop);

        } while (currentNode != stackTop);

        scc.push_back(currentScc);
    }
}

void Graph::CriticalEdgesDFS(int currentNode, int parent, vector <Edge>& cc, vector<int>& order, vector<int>& lowest, int& counter) {

    order[currentNode] = counter;
    lowest[currentNode] = counter;

    ++counter;

    for(auto edge : adjacencyList[currentNode]) {

        int child = edge.destination;

        if(order[child] < 0) {

            CriticalEdgesDFS(child, currentNode, cc, order, lowest, counter);

            lowest[currentNode] = min(lowest[currentNode], lowest[child]);      // Update lowest after subtree is finished

            if (lowest[child] > order[currentNode]) {
                                                            // We can't reach the current node through any path that doesn't include the current edge
                                                            // So current edge is critical
                cc.push_back(edge);            
            }
        }
        else if (child != parent || directed) {                         // Found visited node, so a back-edge
                                                            // The if condition is necessary so we don't go back through the edge we just used
            lowest[currentNode] = min(lowest[currentNode], order[child]);
        }
    }
}

void Graph::BiconnectedDFS(int currentNode, int parent, int currentDepth, vector< vector <int>>& bcc, vector<int>& depth, vector<int>& lowest, stack<int>& nodeStack, vector<bool>& visited) {

    depth[currentNode] =  currentDepth;
    lowest[currentNode] = currentDepth;
    visited[currentNode] = true;
    nodeStack.push(currentNode);

    for(auto edge : adjacencyList[currentNode]) {

        int child = edge.destination;

        if (child != parent) {          // Check is required to prevent loops
                                    
            if(visited[child]) {                    

                lowest[currentNode] = min(lowest[currentNode], depth[child]);               // Back-edge is found
            }

            else {

                BiconnectedDFS(child, currentNode, currentDepth +1, bcc, depth, lowest, nodeStack, visited);

                lowest[currentNode] = min(lowest[currentNode], lowest[child]);              // Update with lowest depth descendants can reach

                if(depth[currentNode] <= lowest[child]) {                                    // If true, then child can't reach above current depth, so current node separates two BCCs

                    
                    vector < int > currentBcc;
                    currentBcc.push_back(currentNode);                  // Current node is part of both BCCs that it separates

                    int stackTop;

                    do {

                        stackTop = nodeStack.top();
                        nodeStack.pop();
                        currentBcc.push_back(stackTop);
                    } while ( stackTop != child );

                    bcc.push_back(currentBcc);
                }
            }
        }
    }
}

void Graph::TopologicalDFS(int currentNode, stack<int>& orderStack, vector<bool>& visitedNodes) {

    visitedNodes[currentNode] = true;

    for(auto edge : adjacencyList[currentNode]) {

        int node = edge.destination;

        if(!visitedNodes[node]) {

            TopologicalDFS(node, orderStack, visitedNodes);
        }
    }
    
    orderStack.push(currentNode);           // The stack is formed in the return order of the DFS
}

#pragma endregion



int main() {

    ifstream inputFile("maxflow.in"); 
    ofstream outputFile("maxflow.out");

    int numberOfNodes, numberOfEdges;

    inputFile >> numberOfNodes >> numberOfEdges;

    Graph graph(numberOfNodes, numberOfEdges, true, false);

    FlowHandler fh(&graph, 0, numberOfNodes-1);

    graph.BuildFlowNetwork(inputFile,fh);

    outputFile << graph.MaximumFlow(0, numberOfNodes-1, fh);
   
    return 0;
}