Open In App

Undirected graph splitting and its application for number pairs

Last Updated : 13 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Graphs can be used for seemingly unconnected problems. Say the problem of cards which have numbers on both side and you try to create a continuous sequence of numbers using one side. This problem leads to problems in how to split a graph into connected subsets with cycles and connected subsets without cycles. 

There are known algorithms to detect if the graph contains a circle. Here we modify it to find all nodes not-connected to circles. We add a Boolean array cyclic initiated as all false, meaning that no node is known to be connected to a circle. 

Then we loop through nodes applied a usual algorithm to determine a circle skipping on nodes that are already known to be connected to a circle. Whenever we find a new node connected to a circle we propagate the property to all connected nodes skipping already designated as connected again. 
Main function is a modification of DFS algorithm from article “Detect cycles in undirected graph“:

All nodes connected to a cycle are designated as such in a linear time. 
The algorithm was applied to the following problem from the recent challenge “Germanium”, which was solved by about 3.5% of participants. 
Suppose we have N cards (100000 >= N >= 1) and on both sides of the cards there is a number ( N are useless and can be discarded. In the same spirit, every car (n, m): n N allows us to put n upwards and ensured its presence in the sequence, while m is useless anyway. 

If we have a card (n.n): n < N, it ensures that n can always be added to a continuous sequence that arrived to n-1. 
If we have two identical cards (n, m) both n, m can be always added to a sequence if needed, just to put it on the opposite side. 

Now we encode cards into a graph in the following way. The graph nodes are supposed to be numbered from 0 to N-1. Every node has a set of edges leading to is represented as a vector, initiated as an empty and overall set of edges is a vector of vectors of integers – edges (N). 
(n, m) : n, m N – discarded. 
(n, m): n N – add to the graph edge (n-1, n-1) 

Now it is easy to understand that every cycle in the graph can be used for all nodes of the graph. A sample: (1, 2) (2, 5), (5, 1) – every number appears on two cards, just pass along the cycle and put any number once up and another time down: 
(1, 2) – 1 up, 2 down 
(2, 5) – 2 up, 5 down 
(5, 1) – 5 up, 1 down 

If any number already put on the upper side, every subsequent card that has the number must be out this number down, so another number can be shown on the upper side. In the graph, it means that any number connected by an edge to a number of cycles is free to be shown. The same is true for a card connected to the card connected to the cycle. So, any number/node that connected somehow to any cycle cannot provide any problem. Pairs of identical cards like (1, 2), (1, 2) also produce a small circle in the graph, the same is true for double cards, like 3, 3 it is a cycle of a node connected to itself. 

All parts of the graph that are not connected to any cycle can be split in not connected smaller graphs. It is more or less obvious that any such fragment has a problematic number – the biggest in the fragment. We use a similar but much easier algorithm to find such fragments and all maximums in fragments. The least of it is Q – the smallest number that cannot be the end of the continuous coverage from the least numbers, the Q above. 

In addition to the cyclic array, we add another array: visitedNonCyclic, meaning that this number already met while passing through a non-cycle fragment. 

The maximum in the non-cycle fragment is extracted by recurrent calls to a function: 
Please refer findMax() in below code. The graph creation is done in solution() of below code. 
After the graph is created the solution just calls to isCyclic and then to: 

// the main crux of the solution, see full code below
graph.isCyclic();
int res = 1 + graph.GetAnswer();

The solution, which was inspired by the rather hard recent “Germanium 2018 Codility Challenge” and brought the gold medal. 

Main Idea: 

  1. Numbers are big, but we demand a continuous sequence from 1 to the last number, 
    but there are no more than 100 000 numbers. So, the biggest possible is really 100 000, take it as the first hypothesis 
  2. Due to the same considerations, the biggest number cannot be bigger than N (amount of numbers) 
  3. We can also pass overall numbers and take the biggest which is NN. The result cannot be bigger than N or NN 
  4. Taking this into account we can replace all cards like (x, 200 000) into (x, x). Both allow setting x upwards, x is not a problem thus. 
  5. Cards with both numbers bigger that N or NN just ignored or disposed 
  6. If there are cards with the same number on both sides it means that the number is always OK, cannot be the smallest problematic, so never the answer. 
  7. If there are two identical cards like (x, y) and (y, x) it means that both x and y are not problematic and cannot be the answer
  8. Now we introduce the main BIG idea that we translate the set of cards into a graph. Vortices of the graph are numbered from 0 to M-1, where M is the last possible number (least of N, NN). Every node has some adjacent nodes, so the set of all edges is a vector of vectors of integers 
  9. Every card like (x, y) with both numbers less or equal to M into edge x-1, y-1, so the first node gets an additional adjacent node and vice versa 
  10. Some vortices will be connected to themselves due to cards like (3, 3). It is a loop case of size 1. 
  11. Some vortices will be connected by two edges or more if there are identical cards. It is a loop of size 2. 
  12. Loops of sizes 1 and 2 have numbers that cannot be of any problem. But now we realize that the same is the case of a cycle of any length. For example, cards are 2, 3; 3, 4; 4, 10; 10, 2; All their numbers have no problem, just put 2, 3, 4, 10 to the upper side. The downside will have 3, 4, 10, 2. 
  13. Now if we have a card x, y and we know that x is guaranteed already in another place, we can put this card x down, y up and ensure that the y is in the resulting sequence. 
  14. So, if any card is connected to a cycle by an edge, it cannot present a problem since the cycle is all OK, so this number is also OK. 
  15. Thus if there is an interconnected subset of nodes of a graph which contains a cycle, all vortices cannot present any problem. 
    So, we can apply one of the known algorithms to find loops like in the article “Detect cycle in an undirected graph” with an addition of propagation 
  16. We can also designate all nodes in the graph that are connected to a cycle, cycled[]. And we can propagate the property 
    Just start with some in a cycle, set it as a cycled, then pass over it’s adjacent skipping the cycled and calling the same function on the adjacent. 
  17. Using a combination of the known algorithm to detect cycles in an undirected graph with skipping on cycled already and propagating the property “connected to a cycle”, we find all nodes connected to cycles. All such nodes are safe, they cannot be a problem 
  18. But remain nodes not connected to any cycle, their problem can be simplified but cutting into separated graphs which have no common edges or nodes. 
  19. But what to do with them? It can be a linear branch or branches that cross one another. All nodes are different. 
  20. With some last effort, one can understand that any such set has only one problematic number – maximum of the set. It can be proven, paying attention that crosses only makes thing better, but the maximum stays still. 
  21. So we cut all non-cycled into connected separated entities and find the maximum in every one. 
  22. Then we find the minimum of them and this is the final answer!

Examples: 

Input : A = [1, 2, 4, 3] B = [1, 3, 2, 3] 
Output : 5. 
Because the cards as they are provide 1, 2, 3, 4 but not 5.

Input : A = [4, 2, 1, 6, 5] B = [3, 2, 1, 7, 7], 
Output: 4. 
Because you can show 3 or 4 by the first card but not both 3 and 4. So, put 3, while 1 and 2 are by the following cards.

Input : A = [2, 3], B = [2, 3] 
Output : 1. Because 1 is missing at all.

Complexity:  

  • expected worst-case time complexity is O(N);
  • expected worst-case space complexity is O(N);

Implementation:

C++




#include <algorithm>
#include <vector>
using namespace std;
 
class Graph {
private:
    int V; // No. of vertices
    vector<vector<int> > edges; // edges grouped by nodes
    bool isCyclicUtil(int v, vector<bool>& visited, int parent);
    vector<bool> cyclic;
    vector<int> maxInNonCyclicFragments;
    int findMax(int v);
    vector<bool> visitedNonCyclic;
    void setPropagateCycle(int v);
 
public:
    Graph(int V); // Constructor
    void addEdge(int v, int w); // to add an edge to graph
 
    // returns true if there is a cycle and
    // also designates all connected to a cycle
    bool isCyclic();
    int getSize() const { return V; }
    int GetAnswer();
};
 
Graph::Graph(int V)
{
    this->V = V;
    edges = vector<vector<int> >(V, vector<int>());
    visitedNonCyclic = cyclic = vector<bool>(V, false);
}
 
void Graph::addEdge(int v, int w)
{
    edges[v].push_back(w);
    edges[w].push_back(v);
}
 
void Graph::setPropagateCycle(int v)
{
    if (cyclic[v])
        return;
    cyclic[v] = true;
    for (auto i = edges[v].begin(); i != edges[v].end(); ++i) {
        setPropagateCycle(*i);
    }
}
 
bool Graph::isCyclicUtil(int v, vector<bool>& visited, int parent)
{
    if (cyclic[v])
        return true;
 
    // Mark the current node as visited
    visited[v] = true;
 
    // Recur for all the vertices edgesacent to this vertex
    vector<int>::iterator i;
    for (i = edges[v].begin(); i != edges[v].end(); ++i) {
 
        // If an edgesacent is not visited, then
        // recur for that edgesacent
        if (!visited[*i]) {
            if (isCyclicUtil(*i, visited, v)) {
                setPropagateCycle(v);
                return true;
            }
        }
 
        // If an edgesacent is visited and not parent
        //  of current vertex, then there is a cycle.
        else if (*i != parent) {
            setPropagateCycle(v);
            return true;
        }
        if (cyclic[*i]) {
            setPropagateCycle(v);
            return true;
        }
    }
    return false;
}
 
bool Graph::isCyclic()
{
    // Mark all the vortices as not visited
    // and not part of recursion stack
    vector<bool> visited(V, false);
 
    // Call the recursive helper function
    // to detect cycle in different DFS trees
    bool res = false;
    for (int u = 0; u < V; u++)
 
        // Don't recur for u if it is already visited{
        if (!visited[u] && !cyclic[u]) {
            if (isCyclicUtil(u, visited, -1)) {
                res = true;
 
                // there was return true originally
                visited = vector<bool>(V, false);
            }
        }
    return res;
}
 
int Graph::findMax(int v)
{
    if (cyclic[v])
        return -1;
    if (visitedNonCyclic.at(v))
        return -1;
    int res = v;
    visitedNonCyclic.at(v) = true;
    for (auto& u2 : edges.at(v)) {
        res = max(res, findMax(u2));
    }
    return res;
}
 
int Graph::GetAnswer()
{
    // cannot be less than, after extract must add 1
    int res = V;
 
    for (int u = 0; u < V; u++) {
        maxInNonCyclicFragments.push_back(findMax(u));
    }
    for (auto& u : maxInNonCyclicFragments) {
        if (u >= 0)
            res = min(res, u);
    }
    return res;
}
 
int solution(vector<int>& A, vector<int>& B)
{
    const int N = (int)A.size();
 
    const int MAX_AMOUNT = 100001;
    vector<bool> present(MAX_AMOUNT, false);
 
    for (auto& au : A) {
        if (au <= N) {
            present.at(au) = true;
        }
    }
    for (auto& au : B) {
        if (au <= N) {
            present.at(au) = true;
        }
    }
    int MAX_POSSIBLE = N;
    for (int i = 1; i <= N; i++) {
        if (false == present.at(i)) {
            MAX_POSSIBLE = i - 1;
            break;
        }
    }
 
    Graph graph(MAX_POSSIBLE);
 
    for (int i = 0; i < N; i++) {
        if (A.at(i) > MAX_POSSIBLE && B.at(i) > MAX_POSSIBLE) {
            continue;
        }
        int mi = min(A.at(i), B.at(i));
        int ma = max(A.at(i), B.at(i));
        if (A.at(i) > MAX_POSSIBLE || B.at(i) > MAX_POSSIBLE) {
            graph.addEdge(mi - 1, mi - 1);
        }
        else {
            graph.addEdge(mi - 1, ma - 1);
        }
    }
    graph.isCyclic();
    int res = 1 + graph.GetAnswer();
    return res;
}
// Test and driver
#include <iostream>
void test(vector<int>& A, vector<int>& B, int expected,
                                 bool printAll = false)
{
    int res = solution(A, B);
    if (expected != res || printAll) {
        for (size_t i = 0; i < A.size(); i++) {
            cout << A.at(i) << " ";
        }
        cout << endl;
        for (size_t i = 0; i < B.size(); i++) {
            cout << B.at(i) << " ";
        }
        cout << endl;
        if (expected != res)
            cout << "Error! Expected: " << expected << "  ";
        else
            cout << "Expected: " << expected << "  ";
    }
    cout << " Result: " << res << endl;
}
int main()
{
    vector<int> VA;
    vector<int> VB;
 
    int A4[] = { 1, 1, 1, 1, 1 };
    int B4[] = { 2, 3, 4, 5, 6 };
    VA = vector<int>(A4, A4 + 1);
    VB = vector<int>(B4, B4 + 1);
    test(VA, VB, 2, true);
 
    int A0[] = { 1, 1 };
    int B0[] = { 2, 2 };
    VA = vector<int>(A0, A0 + 2);
    VB = vector<int>(B0, B0 + 2);
    test(VA, VB, 3);
 
    int A[] = { 1, 2, 4, 3 };
    int B[] = { 1, 3, 2, 3 };
    VA = vector<int>(A, A + 4);
    VB = vector<int>(B, B + 4);
    test(VA, VB, 5);
 
    int A2[] = { 4, 2, 1, 6, 5 };
    int B2[] = { 3, 2, 1, 7, 7 };
    VA = vector<int>(A2, A2 + 5);
    VB = vector<int>(B2, B2 + 5);
    test(VA, VB, 4);
 
    int A3[] = { 2, 3 };
    int B3[] = { 2, 3 };
    VA = vector<int>(A3, A3 + 2);
    VB = vector<int>(B3, B3 + 2);
    test(VA, VB, 1);
    return 0;
}


Java




import java.util.ArrayList;
import java.util.List;
 
class Graph {
    private int V;
    private List<List<Integer>> edges;
    private boolean[] cyclic;
    private boolean[] visitedNonCyclic;
    private List<Integer> maxInNonCyclicFragments;
 
    public Graph(int V) {
        this.V = V;
        edges = new ArrayList<>();
        for (int i = 0; i < V; i++) {
            edges.add(new ArrayList<>());
        }
        visitedNonCyclic = new boolean[V];
        cyclic = new boolean[V];
    }
 
    public void addEdge(int v, int w) {
        edges.get(v).add(w);
        edges.get(w).add(v);
    }
 
    // Propagate the cycle to the connected nodes
    private void setPropagateCycle(int v) {
        if (cyclic[v]) {
            return;
        }
        cyclic[v] = true;
        for (int i : edges.get(v)) {
            setPropagateCycle(i);
        }
    }
 
    // Util method to detect cycles using DFS
    private boolean isCyclicUtil(int v, boolean[] visited, int parent) {
        if (cyclic[v]) {
            return true;
        }
        visited[v] = true;
        for (int i : edges.get(v)) {
            if (!visited[i]) {
                if (isCyclicUtil(i, visited, v)) {
                    setPropagateCycle(v);
                    return true;
                }
            } else if (i != parent) {
                setPropagateCycle(v);
                return true;
            }
            if (cyclic[i]) {
                setPropagateCycle(v);
                return true;
            }
        }
        return false;
    }
 
    // Detects if the graph contains a cycle
    public boolean isCyclic() {
        boolean[] visited = new boolean[V];
        boolean res = false;
        for (int u = 0; u < V; u++) {
            if (!visited[u] && !cyclic[u]) {
                if (isCyclicUtil(u, visited, -1)) {
                    res = true;
                    visited = new boolean[V];
                }
            }
        }
        return res;
    }
 
    // Finds the maximum in the non-cyclic fragments
    private int findMax(int v) {
        if (cyclic[v]) {
            return -1;
        }
        if (visitedNonCyclic[v]) {
            return -1;
        }
        int res = v;
        visitedNonCyclic[v] = true;
        for (int u2 : edges.get(v)) {
            res = Math.max(res, findMax(u2));
        }
        return res;
    }
 
    // Obtains the minimum index among the non-cyclic fragments
    public int getAnswer() {
        int res = V;
        maxInNonCyclicFragments = new ArrayList<>();
        for (int u = 0; u < V; u++) {
            maxInNonCyclicFragments.add(findMax(u));
        }
        for (int u : maxInNonCyclicFragments) {
            if (u >= 0) {
                res = Math.min(res, u);
            }
        }
        return res;
    }
}
 
public class Main {
    public static int solution(List<Integer> A, List<Integer> B) {
        final int N = A.size();
        final int MAX_AMOUNT = 100001;
        boolean[] present = new boolean[MAX_AMOUNT];
        for (int au : A) {
            if (au <= N) {
                present[au] = true;
            }
        }
        for (int au : B) {
            if (au <= N) {
                present[au] = true;
            }
        }
        int MAX_POSSIBLE = N;
        for (int i = 1; i <= N; i++) {
            if (!present[i]) {
                MAX_POSSIBLE = i - 1;
                break;
            }
        }
        Graph graph = new Graph(MAX_POSSIBLE);
        for (int i = 0; i < N; i++) {
            if (A.get(i) > MAX_POSSIBLE && B.get(i) > MAX_POSSIBLE) {
                continue;
            }
            int mi = Math.min(A.get(i), B.get(i));
            int ma = Math.max(A.get(i), B.get(i));
            if (A.get(i) > MAX_POSSIBLE || B.get(i) > MAX_POSSIBLE) {
                graph.addEdge(mi - 1, mi - 1);
            } else {
                graph.addEdge(mi - 1, ma - 1);
            }
        }
        graph.isCyclic();
        int res = 1 + graph.getAnswer();
        return res;
    }
 
    public static void test(List<Integer> A, List<Integer> B, int expected, boolean printAll) {
        int res = solution(A, B);
        if (expected != res || printAll) {
            for (int i = 0; i < A.size(); i++) {
                System.out.print(A.get(i) + " ");
            }
            System.out.println();
            for (int i = 0; i < B.size(); i++) {
                System.out.print(B.get(i) + " ");
            }
            System.out.println();
            if (expected != res) {
                System.out.print("Error! Expected: " + expected + "  ");
            } else {
                System.out.print("Expected: " + expected + "  ");
            }
        }
        System.out.println("Result: " + res);
    }
 
    public static void main(String[] args) {
        List<Integer> VA;
        List<Integer> VB;
        int[] A4 = { 1, 1, 1, 1, 1 };
        int[] B4 = { 2, 3, 4, 5, 6 };
        VA = new ArrayList<>();
        for (int i = 0; i < 1; i++) {
            VA.add(A4[i]);
        }
        VB = new ArrayList<>();
        for (int i = 0; i < 1; i++) {
            VB.add(B4[i]);
        }
        test(VA, VB, 2, true);
        int[] A0 = { 1, 1 };
        int[] B0 = { 2, 2 };
        VA = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            VA.add(A0[i]);
        }
        VB = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            VB.add(B0[i]);
        }
        test(VA, VB, 3, false);
        int[] A = { 1, 2, 4, 3 };
        int[] B = { 1, 3, 2, 3 };
        VA = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            VA.add(A[i]);
        }
        VB = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            VB.add(B[i]);
        }
        test(VA, VB, 5, false);
        int[] A2 = { 4, 2, 1, 6, 5 };
        int[] B2 = { 3, 2, 1, 7, 7 };
        VA = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            VA.add(A2[i]);
        }
        VB = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            VB.add(B2[i]);
        }
        test(VA, VB, 4, false);
        int[] A3 = { 2, 3 };
        int[] B3 = { 2, 3 };
        VA = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            VA.add(A3[i]);
        }
        VB = new ArrayList<>();
        for (int i = 0; i < 2; i++) {
            VB.add(B3[i]);
        }
        test(VA, VB, 1, false);
    }
}


Python3




class Graph:
    def __init__(self, V):
        # Initialize the graph with V vertices
        self.V = V
        self.edges = [[] for _ in range(V)]  # Adjacency list to store edges
        self.visited_non_cyclic = [False] * # Keep track of visited vertices in non-cyclic paths
        self.cyclic = [False] * # Keep track of vertices involved in cycles
        self.max_in_non_cyclic_fragments = []  # Store the maximum in non-cyclic fragments
 
    def add_edge(self, v, w):
        # Add an edge between vertices v and w
        self.edges[v].append(w)
        self.edges[w].append(v)  # Assuming an undirected graph
 
    def set_propagate_cycle(self, v):
        # Mark vertices involved in cycles by propagating the cycle detection
        if self.cyclic[v]:
            return
        self.cyclic[v] = True
        for i in self.edges[v]:
            self.set_propagate_cycle(i)
 
    def is_cyclic_util(self, v, visited, parent):
        # Utility function to detect cycles in the graph using DFS
        if self.cyclic[v]:
            return True
 
        visited[v] = True
 
        for i in self.edges[v]:
            if not visited[i]:
                if self.is_cyclic_util(i, visited, v):
                    self.set_propagate_cycle(v)
                    return True
            elif i != parent:
                self.set_propagate_cycle(v)
                return True
            if self.cyclic[i]:
                self.set_propagate_cycle(v)
                return True
        return False
 
    def is_cyclic(self):
        # Check if the graph contains cycles using DFS traversal
        visited = [False] * self.V
        res = False
        for u in range(self.V):
            if not visited[u] and not self.cyclic[u]:
                if self.is_cyclic_util(u, visited, -1):
                    res = True
                    visited = [False] * self.V
        return res
 
    def find_max(self, v):
        # Find the maximum in non-cyclic fragments using DFS
        if self.cyclic[v]:
            return -1
        if self.visited_non_cyclic[v]:
            return -1
        res = v
        self.visited_non_cyclic[v] = True
        for u2 in self.edges[v]:
            res = max(res, self.find_max(u2))
        return res
 
    def get_answer(self):
        # Calculate the final result by finding the minimum of maximums in non-cyclic fragments
        res = self.V
        for u in range(self.V):
            self.max_in_non_cyclic_fragments.append(self.find_max(u))
        for u in self.max_in_non_cyclic_fragments:
            if u >= 0:
                res = min(res, u)
        return res
 
 
def solution(A, B):
    # Function to solve the problem based on the provided inputs
    N = len(A)
    MAX_AMOUNT = 100001
    present = [False] * MAX_AMOUNT
 
    for au in A:
        if au <= N:
            present[au] = True
    for bu in B:
        if bu <= N:
            present[bu] = True
 
    MAX_POSSIBLE = N
    for i in range(1, N + 1):
        if not present[i]:
            MAX_POSSIBLE = i - 1
            break
 
    graph = Graph(MAX_POSSIBLE)
 
    for i in range(N):
        if A[i] > MAX_POSSIBLE and B[i] > MAX_POSSIBLE:
            continue
        mi = min(A[i], B[i])
        ma = max(A[i], B[i])
        if A[i] > MAX_POSSIBLE or B[i] > MAX_POSSIBLE:
            graph.add_edge(mi - 1, mi - 1)
        else:
            graph.add_edge(mi - 1, ma - 1)
 
    graph.is_cyclic()
    res = 1 + graph.get_answer()
    return res
 
 
def test(A, B, expected, print_all=False):
    # Function to run test cases and display results
    res = solution(A, B)
    if expected != res or print_all:
        print(*A)
        print(*B)
        if expected != res:
            print(f"Error! Expected: {expected}  ", end='')
        else:
            print(f"Expected: {expected}  ", end='')
    print(f"Result: {res}")
 
 
if __name__ == "__main__":
    # Test cases
    test([1], [2], 2, True)
    test([1, 1], [2, 2], 3)
    test([1, 2, 4, 3], [1, 3, 2, 3], 5)
    test([4, 2, 1, 6, 5], [3, 2, 1, 7, 7], 4)
    test([2, 3], [2, 3], 1)


C#




using System;
using System.Collections.Generic;
 
class Graph
{
    private int V;
    private List<List<int>> edges;
    private bool[] cyclic;
    private bool[] visitedNonCyclic;
    private List<int> maxInNonCyclicFragments;
 
    // Constructor for Graph class
    public Graph(int V)
    {
        this.V = V;
        edges = new List<List<int>>();
        for (int i = 0; i < V; i++)
        {
            edges.Add(new List<int>());
        }
        visitedNonCyclic = new bool[V];
        cyclic = new bool[V];
    }
 
    // Method to add an edge to the graph
    public void AddEdge(int v, int w)
    {
        edges[v].Add(w);
        edges[w].Add(v);
    }
 
    // Propagate the cycle to the connected nodes
    private void SetPropagateCycle(int v)
    {
        if (cyclic[v])
        {
            return;
        }
        cyclic[v] = true;
        foreach (int i in edges[v])
        {
            SetPropagateCycle(i);
        }
    }
 
    // Utility method to detect cycles using DFS
    private bool IsCyclicUtil(int v, bool[] visited, int parent)
    {
        if (cyclic[v])
        {
            return true;
        }
        visited[v] = true;
        foreach (int i in edges[v])
        {
            if (!visited[i])
            {
                if (IsCyclicUtil(i, visited, v))
                {
                    SetPropagateCycle(v);
                    return true;
                }
            }
            else if (i != parent)
            {
                SetPropagateCycle(v);
                return true;
            }
            if (cyclic[i])
            {
                SetPropagateCycle(v);
                return true;
            }
        }
        return false;
    }
 
    // Detects if the graph contains a cycle
    public bool IsCyclic()
    {
        bool[] visited = new bool[V];
        bool res = false;
        for (int u = 0; u < V; u++)
        {
            if (!visited[u] && !cyclic[u])
            {
                if (IsCyclicUtil(u, visited, -1))
                {
                    res = true;
                    visited = new bool[V];
                }
            }
        }
        return res;
    }
 
    // Finds the maximum in the non-cyclic fragments
    private int FindMax(int v)
    {
        if (cyclic[v])
        {
            return -1;
        }
        if (visitedNonCyclic[v])
        {
            return -1;
        }
        int res = v;
        visitedNonCyclic[v] = true;
        foreach (int u2 in edges[v])
        {
            res = Math.Max(res, FindMax(u2));
        }
        return res;
    }
 
    // Obtains the minimum index among the non-cyclic fragments
    public int GetAnswer()
    {
        int res = V;
        maxInNonCyclicFragments = new List<int>();
        for (int u = 0; u < V; u++)
        {
            maxInNonCyclicFragments.Add(FindMax(u));
        }
        foreach (int u in maxInNonCyclicFragments)
        {
            if (u >= 0)
            {
                res = Math.Min(res, u);
            }
        }
        return res;
    }
}
 
public class Program
{
    // Main solution method
    public static int Solution(List<int> A, List<int> B)
    {
        int N = A.Count;
        int MAX_AMOUNT = 100001;
        bool[] present = new bool[MAX_AMOUNT];
        foreach (int au in A)
        {
            if (au <= N)
            {
                present[au] = true;
            }
        }
        foreach (int au in B)
        {
            if (au <= N)
            {
                present[au] = true;
            }
        }
        int MAX_POSSIBLE = N;
        for (int i = 1; i <= N; i++)
        {
            if (!present[i])
            {
                MAX_POSSIBLE = i - 1;
                break;
            }
        }
        Graph graph = new Graph(MAX_POSSIBLE);
        for (int i = 0; i < N; i++)
        {
            if (A[i] > MAX_POSSIBLE && B[i] > MAX_POSSIBLE)
            {
                continue;
            }
            int mi = Math.Min(A[i], B[i]);
            int ma = Math.Max(A[i], B[i]);
            if (A[i] > MAX_POSSIBLE || B[i] > MAX_POSSIBLE)
            {
                graph.AddEdge(mi - 1, mi - 1);
            }
            else
            {
                graph.AddEdge(mi - 1, ma - 1);
            }
        }
        graph.IsCyclic();
        int res = 1 + graph.GetAnswer();
        return res;
    }
 
    // Test method to check the solution
    public static void Test(List<int> A, List<int> B, int expected, bool printAll)
    {
        int res = Solution(A, B);
        if (expected != res || printAll)
        {
            for (int i = 0; i < A.Count; i++)
            {
                Console.Write(A[i] + " ");
            }
            Console.WriteLine();
            for (int i = 0; i < B.Count; i++)
            {
                Console.Write(B[i] + " ");
            }
            Console.WriteLine();
            if (expected != res)
            {
                Console.Write("Error! Expected: " + expected + "  ");
            }
            else
            {
                Console.Write("Expected: " + expected + "  ");
            }
        }
        Console.WriteLine("Result: " + res);
    }
 
    // Main method to run tests
    public static void Main(string[] args)
    {
        List<int> VA;
        List<int> VB;
        int[] A4 = { 1, 1, 1, 1, 1 };
        int[] B4 = { 2, 3, 4, 5, 6 };
        VA = new List<int>();
        for (int i = 0; i < 1; i++)
        {
            VA.Add(A4[i]);
        }
        VB = new List<int>();
        for (int i = 0; i < 1; i++)
        {
            VB.Add(B4[i]);
        }
        Test(VA, VB, 2, true);
        int[] A0 = { 1, 1 };
        int[] B0 = { 2, 2 };
        VA = new List<int>();
        for (int i = 0; i < 2; i++)
        {
            VA.Add(A0[i]);
        }
        VB = new List<int>();
        for (int i = 0; i < 2; i++)
        {
            VB.Add(B0[i]);
        }
        Test(VA, VB, 3, false);
        int[] A = { 1, 2, 4, 3 };
        int[] B = { 1, 3, 2, 3 };
        VA = new List<int>();
        for (int i = 0; i < 4; i++)
        {
            VA.Add(A[i]);
        }
        VB = new List<int>();
        for (int i = 0; i < 4; i++)
        {
            VB.Add(B[i]);
        }
        Test(VA, VB, 5, false);
        int[] A2 = { 4, 2, 1, 6, 5 };
        int[] B2 = { 3, 2, 1, 7, 7 };
        VA = new List<int>();
        for (int i = 0; i < 5; i++)
        {
            VA.Add(A2[i]);
        }
        VB = new List<int>();
        for (int i = 0; i < 5; i++)
        {
            VB.Add(B2[i]);
        }
        Test(VA, VB, 4, false);
        int[] A3 = { 2, 3 };
        int[] B3 = { 2, 3 };
        VA = new List<int>();
        for (int i = 0; i < 2; i++)
        {
            VA.Add(A3[i]);
        }
        VB = new List<int>();
        for (int i = 0; i < 2; i++)
        {
            VB.Add(B3[i]);
        }
        Test(VA, VB, 1, false);
    }
}
 
// This code is contirbuted by shivamgupta310570


Javascript




class Graph {
  constructor(V) {
    this.V = V;
    this.edges = Array.from({ length: V }, () => []); // Adjacency list to store edges
    this.visitedNonCyclic = Array(V).fill(false); // Keep track of visited vertices in non-cyclic paths
    this.cyclic = Array(V).fill(false); // Keep track of vertices involved in cycles
    this.maxInNonCyclicFragments = []; // Store the maximum in non-cyclic fragments
  }
 
  addEdge(v, w) {
    this.edges[v].push(w);
    this.edges[w].push(v); // Assuming an undirected graph
  }
 
  // Mark vertices involved in cycles by propagating the cycle detection
  setPropagateCycle(v) {
    if (this.cyclic[v]) return;
    this.cyclic[v] = true;
    for (let i of this.edges[v]) {
      this.setPropagateCycle(i);
    }
  }
 
  // Utility function to detect cycles in the graph using DFS
  isCyclicUtil(v, visited, parent) {
    if (this.cyclic[v]) return true;
    visited[v] = true;
 
    for (let i of this.edges[v]) {
      if (!visited[i]) {
        if (this.isCyclicUtil(i, visited, v)) {
          this.setPropagateCycle(v);
          return true;
        }
      } else if (i !== parent) {
        this.setPropagateCycle(v);
        return true;
      }
      if (this.cyclic[i]) {
        this.setPropagateCycle(v);
        return true;
      }
    }
    return false;
  }
 
  // Check if the graph contains cycles using DFS traversal
  isCyclic() {
    const visited = Array(this.V).fill(false);
    let res = false;
    for (let u = 0; u < this.V; u++) {
      if (!visited[u] && !this.cyclic[u]) {
        if (this.isCyclicUtil(u, visited, -1)) {
          res = true;
          visited.fill(false);
        }
      }
    }
    return res;
  }
 
  // Find the maximum in non-cyclic fragments using DFS
  findMax(v) {
    if (this.cyclic[v]) return -1;
    if (this.visitedNonCyclic[v]) return -1;
    let res = v;
    this.visitedNonCyclic[v] = true;
    for (let u2 of this.edges[v]) {
      res = Math.max(res, this.findMax(u2));
    }
    return res;
  }
 
  // Calculate the final result by finding the minimum of maximums in non-cyclic fragments
  getAnswer() {
    let res = this.V;
    for (let u = 0; u < this.V; u++) {
      this.maxInNonCyclicFragments.push(this.findMax(u));
    }
    for (let u of this.maxInNonCyclicFragments) {
      if (u >= 0) {
        res = Math.min(res, u);
      }
    }
    return res;
  }
}
 
function solution(A, B) {
  const N = A.length;
  const MAX_AMOUNT = 100001;
  const present = new Array(MAX_AMOUNT).fill(false);
 
  // Mark the presence of elements in A and B arrays
  for (let au of A) {
    if (au <= N) {
      present[au] = true;
    }
  }
  for (let bu of B) {
    if (bu <= N) {
      present[bu] = true;
    }
  }
 
  let MAX_POSSIBLE = N;
  // Find the maximum possible value based on the present array
  for (let i = 1; i <= N; i++) {
    if (!present[i]) {
      MAX_POSSIBLE = i - 1;
      break;
    }
  }
 
  const graph = new Graph(MAX_POSSIBLE);
 
  // Add edges to the graph based on A and B arrays
  for (let i = 0; i < N; i++) {
    if (A[i] > MAX_POSSIBLE && B[i] > MAX_POSSIBLE) {
      continue;
    }
    const mi = Math.min(A[i], B[i]);
    const ma = Math.max(A[i], B[i]);
    if (A[i] > MAX_POSSIBLE || B[i] > MAX_POSSIBLE) {
      graph.addEdge(mi - 1, mi - 1);
    } else {
      graph.addEdge(mi - 1, ma - 1);
    }
  }
 
  graph.isCyclic();
  const res = 1 + graph.getAnswer();
  return res;
}
 
function test(A, B, expected, printAll = false) {
  const res = solution(A, B);
  if (expected !== res || printAll) {
    console.log(...A);
    console.log(...B);
    if (expected !== res) {
      console.log(`Error! Expected: ${expected}  `);
    } else {
      console.log(`Expected: ${expected}  `);
    }
  }
  console.log(`Result: ${res}`);
}
 
// Test cases
test([1], [2], 2, true);
test([1, 1], [2, 2], 3);
test([1, 2, 4, 3], [1, 3, 2, 3], 5);
test([4, 2, 1, 6, 5], [3, 2, 1, 7, 7], 4);
test([2, 3], [2, 3], 1);


Output

1 
2 
Expected: 2   Result: 2
 Result: 3
 Result: 5
 Result: 4
 Result: 1











Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads