Geek is a chemical scientist performing an experiment to find an antidote to a poison. The experiment requires mixing different solutions in a specific order.
- He is given an array
mix, wheremix[i] = [X, Y]denotes that solutionsXandYneed to be mixed. - He is also given an array dangerous pairs are given in the array
danger, wheredanger[i] = [P, Q]indicates that if solutionsPandQbecome part of the same connected mixture, an explosion will occur. - For each pair in
mix, determine whether it is safe to perform that mixing operation.
Return a boolean array answer of size n, where:
answer[i] = trueif mixing the solutions inmix[i]is safe.answer[i] = falseif performing the mix would cause an explosion.
Notes:
- The mixing operations must be processed in the given order.
- If a mixing operation would cause an explosion, that operation is rejected and the corresponding solutions are not merged.
- Only successful mixing operations affect future operations.
- Two solutions are considered to be in the same flask if they belong to the same connected component formed by the previously accepted mixing operations.
Examples:
Input: mix = [[1, 2], [2, 3], [4, 5], [3, 5], [2, 4]], danger = [[1, 3], [4, 2]]
Output: [true, false, true, true, false]
Explanation:
- Mixing solutions
1and2is safe, so answer[0] =true.- Mixing solutions
2and3is not allowed because solution1is already connected with2, and mixing2with3would place dangerous pair(1, 3)in the same group. Therefore, answer[1] =false.- Mixing solutions
4and5is safe, so answer[2] =true.- Mixing solutions
3and5is also safe and does not create any dangerous combination. Therefore, answer[3] =true.- Mixing solutions
2and4is not allowed because it would place the dangerous pair(2, 4)in the same connected group. Therefore, answer[4] =false.Input: mix = [[1, 2], [2, 3], [1, 3]], danger = [[1, 2], [1, 3]]
Output: [false, true, false]
Explanation:
- Mixing solutions
1and2is directly dangerous since(1, 2)is present in thedangerlist. Therefore, answer[0] =false.- Mixing solutions
2and3does not create any dangerous pair in the same group, so answer[1] =true.- Mixing solutions
1and3is directly dangerous since(1, 3)is present in thedangerlist. Therefore, answer[2] =false.
Table of Content
[Naive Approach] Using Graph + DFS Connectivity Check - O(M × D × (V + E)) Time and O(V + E) Space
The idea is to process each mixing operation one by one and maintain a graph where nodes represent solutions and edges represent successful mixes. For every new mix, the edge is temporarily added to the graph. Then, for each dangerous pair, DFS is used to check whether both solutions become connected. If any dangerous pair gets connected, an explosion occurs, so the current mix is rejected and the added edge is removed. Otherwise, the mix is accepted.
- Create a graph of solutions
- Process each mixing request: Temporarily add the edge between the two solutions
- For every dangerous pair: Run DFS to check connectivity
- If any dangerous pair becomes connected: Reject the mix and Remove the added edge
- Otherwise: Accept the mix
- Store the result of every mixing operation
#include <bits/stdc++.h>
using namespace std;
// Function to check whether two solutions
// belong to the same connected component
bool dfs(int src, int dest, vector<vector<int>> &adj, vector<bool> &vis)
{
// Destination reached
if (src == dest)
return true;
vis[src] = true;
// Visit all neighbouring solutions
for (int nbr : adj[src])
{
if (!vis[nbr] && dfs(nbr, dest, adj, vis))
return true;
}
return false;
}
// Function to process all mixing operations
vector<string> avoidExp(vector<vector<int>> &mix, vector<vector<int>> &danger)
{
// Find maximum solution number
int mx = 0;
for (auto &x : mix)
{
mx = max(mx, x[0]);
mx = max(mx, x[1]);
}
for (auto &x : danger)
{
mx = max(mx, x[0]);
mx = max(mx, x[1]);
}
// Graph representing successful mixes
vector<vector<int>> adj(mx + 1);
vector<string> ans;
// Process each mixing request
for (auto &m : mix)
{
int u = m[0];
int v = m[1];
// Temporarily mix the solutions
adj[u].push_back(v);
adj[v].push_back(u);
bool safe = true;
// Check every dangerous pair
for (auto &d : danger)
{
vector<bool> vis(mx + 1, false);
// If dangerous solutions become connected,
// explosion occurs
if (dfs(d[0], d[1], adj, vis))
{
safe = false;
break;
}
}
if (safe)
ans.push_back("true");
else
{
ans.push_back("false");
// Undo the mix operation
adj[u].pop_back();
adj[v].pop_back();
}
}
return ans;
}
int main()
{
vector<vector<int>> mix = {{1, 2}, {2, 3}, {4, 5}, {1, 5}};
vector<vector<int>> danger = {{1, 3}, {2, 5}};
vector<string> ans = avoidExp(mix, danger);
for (string x : ans)
cout << x << " ";
}
import java.util.*;
class GfG {
// Function to check whether two solutions
// belong to the same connected component
private boolean dfs(int src, int dest, List<List<Integer>> adj, boolean[] vis) {
// Destination reached
if (src == dest)
return true;
vis[src] = true;
// Visit all neighbouring solutions
for (int nbr : adj.get(src)) {
if (!vis[nbr] && dfs(nbr, dest, adj, vis))
return true;
}
return false;
}
// Function to process all mixing operations
public List<String> avoidExp(int[][] mix, int[][] danger) {
// Find maximum solution number
int mx = 0;
for (int[] x : mix) {
mx = Math.max(mx, x[0]);
mx = Math.max(mx, x[1]);
}
for (int[] x : danger) {
mx = Math.max(mx, x[0]);
mx = Math.max(mx, x[1]);
}
// Graph representing successful mixes
List<List<Integer>> adj = new ArrayList<>();
for (int i = 0; i <= mx; i++) {
adj.add(new ArrayList<>());
}
List<String> ans = new ArrayList<>();
// Process each mixing request
for (int[] m : mix) {
int u = m[0];
int v = m[1];
// Temporarily mix the solutions
adj.get(u).add(v);
adj.get(v).add(u);
boolean safe = true;
// Check every dangerous pair
for (int[] d : danger) {
boolean[] vis = new boolean[mx + 1];
// If dangerous solutions become connected,
// explosion occurs
if (dfs(d[0], d[1], adj, vis)) {
safe = false;
break;
}
}
if (safe) {
ans.add("true");
} else {
ans.add("false");
// Undo the mix operation
adj.get(u).remove(adj.get(u).size() - 1);
adj.get(v).remove(adj.get(v).size() - 1);
}
}
return ans;
}
public static void main(String[] args) {
GfG sol = new GfG();
int[][] mix = {{1, 2}, {2, 3}, {4, 5}, {1, 5}};
int[][] danger = {{1, 3}, {2, 5}};
List<String> ans = sol.avoidExp(mix, danger);
for (String x : ans) {
System.out.print(x + " ");
}
}
}
# Python program to check if mixing solutions causes explosion
# Function to check whether two solutions belong to the same connected component
def dfs(src, dest, adj, vis):
# Destination reached
if src == dest:
return True
vis[src] = True
# Visit all neighbouring solutions
for nbr in adj[src]:
if not vis[nbr] and dfs(nbr, dest, adj, vis):
return True
return False
# Function to process all mixing operations
def avoidExp(mix, danger):
# Find maximum solution number
mx = 0
for x in mix:
mx = max(mx, x[0], x[1])
for x in danger:
mx = max(mx, x[0], x[1])
# Graph representing successful mixes
adj = [[] for _ in range(mx + 1)]
ans = []
# Process each mixing request
for u, v in mix:
# Temporarily mix the solutions
adj[u].append(v)
adj[v].append(u)
safe = True
# Check every dangerous pair
for a, b in danger:
vis = [False] * (mx + 1)
# If dangerous solutions become connected, explosion occurs
if dfs(a, b, adj, vis):
safe = False
break
if safe:
ans.append("true")
else:
ans.append("false")
# Undo the mix operation
adj[u].pop()
adj[v].pop()
return ans
# Driver code
if __name__ == "__main__":
mix = [[1, 2], [2, 3], [4, 5], [1, 5]]
danger = [[1, 3], [2, 5]]
ans = avoidExp(mix, danger)
print(' '.join(ans))
// C# program to check if mixing solutions causes explosion
using System;
using System.Collections.Generic;
class GfG {
// Function to check whether two solutions belong to the same connected component
static bool dfs(int src, int dest, List<List<int>> adj, bool[] vis) {
// Destination reached
if (src == dest)
return true;
vis[src] = true;
// Visit all neighbouring solutions
foreach (int nbr in adj[src]) {
if (!vis[nbr] && dfs(nbr, dest, adj, vis))
return true;
}
return false;
}
// Function to process all mixing operations
public List<string> avoidExp(int[,] mix, int[,] danger) {
// Find maximum solution number
int mx = 0;
int mixRows = mix.GetLength(0);
int dangerRows = danger.GetLength(0);
for (int i = 0; i < mixRows; i++) {
mx = Math.Max(mx, mix[i, 0]);
mx = Math.Max(mx, mix[i, 1]);
}
for (int i = 0; i < dangerRows; i++) {
mx = Math.Max(mx, danger[i, 0]);
mx = Math.Max(mx, danger[i, 1]);
}
// Graph representing successful mixes
List<List<int>> adj = new List<List<int>>();
for (int i = 0; i <= mx; i++) {
adj.Add(new List<int>());
}
List<string> ans = new List<string>();
// Process each mixing request
for (int i = 0; i < mixRows; i++) {
int u = mix[i, 0];
int v = mix[i, 1];
// Temporarily mix the solutions
adj[u].Add(v);
adj[v].Add(u);
bool safe = true;
// Check every dangerous pair
for (int j = 0; j < dangerRows; j++) {
bool[] vis = new bool[mx + 1];
// If dangerous solutions become connected, explosion occurs
if (dfs(danger[j, 0], danger[j, 1], adj, vis)) {
safe = false;
break;
}
}
if (safe) {
ans.Add("true");
} else {
ans.Add("false");
// Undo the mix operation
adj[u].RemoveAt(adj[u].Count - 1);
adj[v].RemoveAt(adj[v].Count - 1);
}
}
return ans;
}
static void Main(string[] args) {
GfG sol = new GfG();
int[,] mix = {
{1, 2}, {2, 3}, {4, 5}, {1, 5}
};
int[,] danger = {
{1, 3}, {2, 5}
};
List<string> ans = sol.avoidExp(mix, danger);
foreach (string x in ans) {
Console.Write(x + " ");
}
}
}
// JavaScript program to check if mixing solutions causes explosion
// Function to check whether two solutions belong to the same connected component
function dfs(src, dest, adj, vis) {
// Destination reached
if (src === dest)
return true;
vis[src] = true;
// Visit all neighbouring solutions
for (let nbr of adj[src]) {
if (!vis[nbr] && dfs(nbr, dest, adj, vis))
return true;
}
return false;
}
// Function to process all mixing operations
function avoidExp(mix, danger) {
// Find maximum solution number
let mx = 0;
for (let x of mix) {
mx = Math.max(mx, x[0], x[1]);
}
for (let x of danger) {
mx = Math.max(mx, x[0], x[1]);
}
// Graph representing successful mixes
let adj = Array(mx + 1);
for (let i = 0; i <= mx; i++) {
adj[i] = [];
}
let ans = [];
// Process each mixing request
for (let m of mix) {
let u = m[0];
let v = m[1];
// Temporarily mix the solutions
adj[u].push(v);
adj[v].push(u);
let safe = true;
// Check every dangerous pair
for (let d of danger) {
let vis = Array(mx + 1).fill(false);
// If dangerous solutions become connected, explosion occurs
if (dfs(d[0], d[1], adj, vis)) {
safe = false;
break;
}
}
if (safe) {
ans.push("true");
} else {
ans.push("false");
// Undo the mix operation
adj[u].pop();
adj[v].pop();
}
}
return ans;
}
// Driver code
const mix = [[1, 2], [2, 3], [4, 5], [1, 5]];
const danger = [[1, 3], [2, 5]];
const ans = avoidExp(mix, danger);
console.log(ans.join(' '));
Output
1 0 1 0
[Optimized Approach] Using DSU with Path Compression - O(M × D × α(V)) Time and O(V) Space
The idea is to use Disjoint Set Union (DSU) with Path Compression to efficiently maintain connected groups of solutions. Each solution initially belongs to its own set. For every mixing operation, the representatives of the two solution groups are found. Before merging them, all dangerous pairs are checked to ensure that the merge will not place the two solutions of any dangerous pair in the same connected component. Path Compression makes future parent lookups much faster by directly connecting nodes to their ultimate parent, improving the efficiency of DSU operations.
- Initialize each solution as a separate DSU set
- Use Path Compression in the
findParent()operation - For each mixing request: Find representatives of both solutions and Check all dangerous pairs
- If a dangerous pair would become connected, reject the merge Otherwise, union the two sets
- Store the result of every mixing operation
// C++ program to solve Avoid Explosion Problem
// using DSU with Path Compression
#include <bits/stdc++.h>
using namespace std;
// Function to find the ultimate parent
// of a solution
int findParent(int x, vector<int> &parent)
{
// Path Compression:
// directly connect node to its root
if (parent[x] == x)
return x;
return parent[x] = findParent(parent[x], parent);
}
// Function to merge two groups
void Union(int a, int b, vector<int> &parent)
{
int pa = findParent(a, parent);
int pb = findParent(b, parent);
// Merge only if both groups are different
if (pa != pb)
parent[pa] = pb;
}
// Function to process all mixing operations
vector<string> avoidExp(vector<vector<int>> &mix, vector<vector<int>> &danger)
{
// Find maximum solution number
int mx = 0;
for (auto &x : mix)
{
mx = max(mx, x[0]);
mx = max(mx, x[1]);
}
for (auto &x : danger)
{
mx = max(mx, x[0]);
mx = max(mx, x[1]);
}
// Initially every solution
// belongs to a separate group
vector<int> parent(mx + 1);
for (int i = 0; i <= mx; i++)
parent[i] = i;
vector<string> ans;
// Process each mixing request
for (auto &m : mix)
{
int u = m[0];
int v = m[1];
int pu = findParent(u, parent);
int pv = findParent(v, parent);
bool safe = true;
// Check all dangerous pairs
for (auto &d : danger)
{
int a = findParent(d[0], parent);
int b = findParent(d[1], parent);
// If current merge would connect
// a dangerous pair, reject it
if ((pu == a && pv == b) || (pu == b && pv == a))
{
safe = false;
break;
}
}
if (safe)
{
// Perform the merge
Union(u, v, parent);
ans.push_back("true");
}
else
{
// Reject the merge
ans.push_back("false");
}
}
return ans;
}
int main()
{
vector<vector<int>> mix = {{1, 2}, {2, 3}, {4, 5}, {1, 5}};
vector<vector<int>> danger = {{1, 3}, {2, 5}};
vector<string> ans = avoidExp(mix, danger);
for (string x : ans)
cout << x << " ";
}
// Java program to solve Avoid Explosion Problem using DSU with Path Compression
import java.util.*;
class GfG {
// Function to find the ultimate parent of a solution with Path Compression
static int findParent(int x, int[] parent) {
// Path Compression: directly connect node to its root
if (parent[x] == x)
return x;
return parent[x] = findParent(parent[x], parent);
}
// Function to merge two groups
static void union(int a, int b, int[] parent) {
int pa = findParent(a, parent);
int pb = findParent(b, parent);
// Merge only if both groups are different
if (pa != pb)
parent[pa] = pb;
}
// Function to process all mixing operations
public List<String> avoidExp(int[][] mix, int[][] danger) {
// Find maximum solution number
int mx = 0;
for (int[] x : mix) {
mx = Math.max(mx, x[0]);
mx = Math.max(mx, x[1]);
}
for (int[] x : danger) {
mx = Math.max(mx, x[0]);
mx = Math.max(mx, x[1]);
}
// Initially every solution belongs to a separate group
int[] parent = new int[mx + 1];
for (int i = 0; i <= mx; i++) {
parent[i] = i;
}
List<String> ans = new ArrayList<>();
// Process each mixing request
for (int[] m : mix) {
int u = m[0];
int v = m[1];
int pu = findParent(u, parent);
int pv = findParent(v, parent);
boolean safe = true;
// Check all dangerous pairs
for (int[] d : danger) {
int a = findParent(d[0], parent);
int b = findParent(d[1], parent);
// If current merge would connect a dangerous pair, reject it
if ((pu == a && pv == b) || (pu == b && pv == a)) {
safe = false;
break;
}
}
if (safe) {
// Perform the merge
union(u, v, parent);
ans.add("true");
} else {
// Reject the merge
ans.add("false");
}
}
return ans;
}
public static void main(String[] args) {
GfG sol = new GfG();
int[][] mix = {
{1, 2}, {2, 3}, {4, 5}, {1, 5}
};
int[][] danger = {
{1, 3}, {2, 5}
};
List<String> ans = sol.avoidExp(mix, danger);
for (String x : ans) {
System.out.print(x + " ");
}
}
}
# Python program to solve Avoid Explosion Problem using DSU with Path Compression
# Function to find the ultimate parent of a solution with Path Compression
def findParent(x, parent):
# Path Compression: directly connect node to its root
if parent[x] == x:
return x
parent[x] = findParent(parent[x], parent)
return parent[x]
# Function to merge two groups
def union(a, b, parent):
pa = findParent(a, parent)
pb = findParent(b, parent)
# Merge only if both groups are different
if pa != pb:
parent[pa] = pb
# Function to process all mixing operations
def avoidExp(mix, danger):
# Find maximum solution number
mx = 0
for x in mix:
mx = max(mx, x[0], x[1])
for x in danger:
mx = max(mx, x[0], x[1])
# Initially every solution belongs to a separate group
parent = list(range(mx + 1))
ans = []
# Process each mixing request
for u, v in mix:
pu = findParent(u, parent)
pv = findParent(v, parent)
safe = True
# Check all dangerous pairs
for a, b in danger:
pa = findParent(a, parent)
pb = findParent(b, parent)
# If current merge would connect a dangerous pair, reject it
if (pu == pa and pv == pb) or (pu == pb and pv == pa):
safe = False
break
if safe:
# Perform the merge
union(u, v, parent)
ans.append("true")
else:
# Reject the merge
ans.append("false")
return ans
# Driver code
if __name__ == "__main__":
mix = [[1, 2], [2, 3], [4, 5], [1, 5]]
danger = [[1, 3], [2, 5]]
ans = avoidExp(mix, danger)
print(' '.join(ans))
// C# program to solve Avoid Explosion Problem using DSU with Path Compression
using System;
using System.Collections.Generic;
class GfG {
// Function to find the ultimate parent of a solution with Path Compression
static int findParent(int x, int[] parent) {
// Path Compression: directly connect node to its root
if (parent[x] == x)
return x;
parent[x] = findParent(parent[x], parent);
return parent[x];
}
// Function to merge two groups
static void union(int a, int b, int[] parent) {
int pa = findParent(a, parent);
int pb = findParent(b, parent);
// Merge only if both groups are different
if (pa != pb)
parent[pa] = pb;
}
// Function to process all mixing operations
public List<string> avoidExp(int[,] mix, int[,] danger) {
// Find maximum solution number
int mx = 0;
int mixRows = mix.GetLength(0);
int dangerRows = danger.GetLength(0);
for (int i = 0; i < mixRows; i++) {
mx = Math.Max(mx, mix[i, 0]);
mx = Math.Max(mx, mix[i, 1]);
}
for (int i = 0; i < dangerRows; i++) {
mx = Math.Max(mx, danger[i, 0]);
mx = Math.Max(mx, danger[i, 1]);
}
// Initially every solution belongs to a separate group
int[] parent = new int[mx + 1];
for (int i = 0; i <= mx; i++) {
parent[i] = i;
}
List<string> ans = new List<string>();
// Process each mixing request
for (int i = 0; i < mixRows; i++) {
int u = mix[i, 0];
int v = mix[i, 1];
int pu = findParent(u, parent);
int pv = findParent(v, parent);
bool safe = true;
// Check all dangerous pairs
for (int j = 0; j < dangerRows; j++) {
int a = findParent(danger[j, 0], parent);
int b = findParent(danger[j, 1], parent);
// If current merge would connect a dangerous pair, reject it
if ((pu == a && pv == b) || (pu == b && pv == a)) {
safe = false;
break;
}
}
if (safe) {
// Perform the merge
union(u, v, parent);
ans.Add("true");
} else {
// Reject the merge
ans.Add("false");
}
}
return ans;
}
static void Main(string[] args) {
GfG sol = new GfG();
int[,] mix = {
{1, 2}, {2, 3}, {4, 5}, {1, 5}
};
int[,] danger = {
{1, 3}, {2, 5}
};
List<string> ans = sol.avoidExp(mix, danger);
foreach (string x in ans) {
Console.Write(x + " ");
}
}
}
// JavaScript program to solve Avoid Explosion Problem using DSU with Path Compression
// Function to find the ultimate parent of a solution with Path Compression
function findParent(x, parent) {
// Path Compression: directly connect node to its root
if (parent[x] === x)
return x;
parent[x] = findParent(parent[x], parent);
return parent[x];
}
// Function to merge two groups
function union(a, b, parent) {
let pa = findParent(a, parent);
let pb = findParent(b, parent);
// Merge only if both groups are different
if (pa !== pb)
parent[pa] = pb;
}
// Function to process all mixing operations
function avoidExp(mix, danger) {
// Find maximum solution number
let mx = 0;
for (let x of mix) {
mx = Math.max(mx, x[0], x[1]);
}
for (let x of danger) {
mx = Math.max(mx, x[0], x[1]);
}
// Initially every solution belongs to a separate group
let parent = Array(mx + 1);
for (let i = 0; i <= mx; i++) {
parent[i] = i;
}
let ans = [];
// Process each mixing request
for (let m of mix) {
let u = m[0];
let v = m[1];
let pu = findParent(u, parent);
let pv = findParent(v, parent);
let safe = true;
// Check all dangerous pairs
for (let d of danger) {
let a = findParent(d[0], parent);
let b = findParent(d[1], parent);
// If current merge would connect a dangerous pair, reject it
if ((pu === a && pv === b) || (pu === b && pv === a)) {
safe = false;
break;
}
}
if (safe) {
// Perform the merge
union(u, v, parent);
ans.push("true");
} else {
// Reject the merge
ans.push("false");
}
}
return ans;
}
// Driver code
const mix = [[1, 2], [2, 3], [4, 5], [1, 5]];
const danger = [[1, 3], [2, 5]];
const ans = avoidExp(mix, danger);
console.log(ans.join(' '));
Output
1 0 1 0