Given two integer arrays arr1[] and arr2[] of same size, where a bridge can be built between point arr1[i] on one bank of a river and point arr2[i] on the opposite bank.
Find the maximum number of bridges that can be constructed such that no two bridges cross each other.
Bridges sharing an endpoint are allowed and are not considered crossing.
It is guaranteed that there is no pair of distinct indices i and j such that both arr1[i] == arr1[j] and arr2[i] == arr2[j]; that is, no two bridges connect the same pair of endpoints.
Examples:
Input: arr1[] = [3, 1, 4, 4], arr2[] = [1, 3, 2, 1]
Output: 3
Explanation: One valid set of non-crossing bridges is (3,1), (4,1), and (4,2). It is not possible to construct more than 3 bridges without creating a crossing.
Input: arr1[] = [1, 1], arr2[] = [1, 2]
Output: 2
Explanation: The two bridges share an endpoint but do not cross each other. Hence, both bridges can be constructed.
Table of Content
[Naive Approach] Generate All Possible Bridge Subsets - O(2 ^ n * n ^ 2) Time and O(n) Space
The idea is to generate every possible subset of bridges and check whether any two selected bridges cross each other. Among all valid subsets, return the maximum number of bridges.
#include <iostream>
#include <vector>
using namespace std;
// Function to check whether selected
// bridges are non-crossing
bool isValid(vector<pair<int, int>> &bridges)
{
int m = bridges.size();
// Compare every pair of bridges
for (int i = 0; i < m; i++)
{
for (int j = i + 1; j < m; j++)
{
// Check if two bridges cross
if ((bridges[i].first < bridges[j].first && bridges[i].second > bridges[j].second) ||
(bridges[i].first > bridges[j].first && bridges[i].second < bridges[j].second))
return false;
}
}
return true;
}
int maxBridges(vector<int> &arr1, vector<int> &arr2)
{
int n = arr1.size();
int ans = 0;
// Generate every subset
for (int mask = 0; mask < (1 << n); mask++)
{
vector<pair<int, int>> bridges;
for (int i = 0; i < n; i++)
{
if (mask & (1 << i))
bridges.push_back({arr1[i], arr2[i]});
}
if (isValid(bridges))
ans = max(ans, (int)bridges.size());
}
return ans;
}
int main()
{
vector<int> arr1 = {3, 1, 4, 4};
vector<int> arr2 = {1, 3, 2, 1};
cout << maxBridges(arr1, arr2);
return 0;
}
import java.util.ArrayList;
class GFG {
// Function to check whether selected bridges are
// non-crossing
static boolean isValid(ArrayList<int[]> bridges)
{
int m = bridges.size();
// Compare every pair of bridges
for (int i = 0; i < m; i++) {
for (int j = i + 1; j < m; j++) {
// Check if two bridges cross
if ((bridges.get(i)[0] < bridges.get(j)[0]
&& bridges.get(i)[1]
> bridges.get(j)[1])
|| (bridges.get(i)[0]
> bridges.get(j)[0]
&& bridges.get(i)[1]
< bridges.get(j)[1]))
return false;
}
}
return true;
}
public int maxBridges(int[] arr1, int[] arr2)
{
int n = arr1.length;
int ans = 0;
// Generate every subset
for (int mask = 0; mask < (1 << n); mask++) {
ArrayList<int[]> bridges = new ArrayList<>();
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0)
bridges.add(
new int[] { arr1[i], arr2[i] });
}
if (isValid(bridges))
ans = Math.max(ans, bridges.size());
}
return ans;
}
public static void main(String[] args)
{
int[] arr1 = { 3, 1, 4, 4 };
int[] arr2 = { 1, 3, 2, 1 };
GFG obj = new GFG();
System.out.println(obj.maxBridges(arr1, arr2));
}
}
# Function to check whether selected
# bridges are non-crossing
def isValid(bridges):
m = len(bridges)
# Compare every pair of bridges
for i in range(m):
for j in range(i + 1, m):
# Check if two bridges cross
if ((bridges[i][0] < bridges[j][0] and bridges[i][1] > bridges[j][1]) or
(bridges[i][0] > bridges[j][0] and bridges[i][1] < bridges[j][1])):
return False
return True
def maxBridges(arr1, arr2):
n = len(arr1)
ans = 0
# Generate every subset
for mask in range(1 << n):
bridges = []
for i in range(n):
if mask & (1 << i):
bridges.append((arr1[i], arr2[i]))
if isValid(bridges):
ans = max(ans, len(bridges))
return ans
if __name__ == "__main__":
arr1 = [3, 1, 4, 4]
arr2 = [1, 3, 2, 1]
print(maxBridges(arr1, arr2))
using System;
using System.Collections.Generic;
class GFG {
// Function to check whether selected bridges are
// non-crossing
static bool IsValid(List<(int, int)> bridges)
{
int m = bridges.Count;
// Compare every pair of bridges
for (int i = 0; i < m; i++) {
for (int j = i + 1; j < m; j++) {
// Check if two bridges cross
if ((bridges[i].Item1 < bridges[j].Item1
&& bridges[i].Item2 > bridges[j].Item2)
|| (bridges[i].Item1 > bridges[j].Item1
&& bridges[i].Item2
< bridges[j].Item2))
return false;
}
}
return true;
}
public int maxBridges(int[] arr1, int[] arr2)
{
int n = arr1.Length;
int ans = 0;
// Generate every subset
for (int mask = 0; mask < (1 << n); mask++) {
List<(int, int)> bridges
= new List<(int, int)>();
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0)
bridges.Add((arr1[i], arr2[i]));
}
if (IsValid(bridges))
ans = Math.Max(ans, bridges.Count);
}
return ans;
}
static void Main()
{
int[] arr1 = { 3, 1, 4, 4 };
int[] arr2 = { 1, 3, 2, 1 };
GFG obj = new GFG();
Console.WriteLine(obj.maxBridges(arr1, arr2));
}
}
// Function to check whether selected bridges are
// non-crossing
function isValid(bridges)
{
let m = bridges.length;
// Compare every pair of bridges
for (let i = 0; i < m; i++) {
for (let j = i + 1; j < m; j++) {
// Check if two bridges cross
if ((bridges[i][0] < bridges[j][0]
&& bridges[i][1] > bridges[j][1])
|| (bridges[i][0] > bridges[j][0]
&& bridges[i][1] < bridges[j][1]))
return false;
}
}
return true;
}
function maxBridges(arr1, arr2)
{
let n = arr1.length;
let ans = 0;
// Generate every subset
for (let mask = 0; mask < (1 << n); mask++) {
let bridges = [];
for (let i = 0; i < n; i++) {
if (mask & (1 << i))
bridges.push([ arr1[i], arr2[i] ]);
}
if (isValid(bridges))
ans = Math.max(ans, bridges.length);
}
return ans;
}
// Driver code
let arr1 = [ 3, 1, 4, 4 ];
let arr2 = [ 1, 3, 2, 1 ];
console.log(maxBridges(arr1, arr2));
Output
3
[Better Approach] Using Sorting with Dynamic Programming (LNDS) - O(n ^ 2) Time and O(n) Space
The idea is to first sort all the bridges by their first endpoints, and by their second endpoints in case of a tie. After sorting, use dynamic programming to find the Longest Non-Decreasing Subsequence (LNDS) of the second bank endpoints, where dp[i] stores the maximum number of non-crossing bridges ending at the i-th bridge. The maximum value in dp gives the maximum number of non-crossing bridges.
Let us understand with an example:
Input: arr1[] = [3, 1, 4, 4], arr2[] = [1, 3, 2, 1]
- Form the bridge pairs: (3,1), (1,3), (4,2), (4,1) and sort them to get (1,3), (3,1), (4,1), (4,2).
- Initialize dp = [1, 1, 1, 1] since each bridge alone can form a valid set.
- For bridge (3,1), no previous bridge has a second endpoint <= 1, so dp = [1, 1, 1, 1].
- For bridge (4,1), bridge (3,1) satisfies 1 <= 1, so update dp = [1, 1, 2, 1].
- For bridge (4,2), bridge (3,1) and (4,1) satisfy the condition, giving dp = [1, 1, 2, 3]. The maximum value in dp is 3, so the answer is 3.
#include <iostream>
#include <vector>
using namespace std;
int maxBridges(vector<int> &arr1, vector<int> &arr2)
{
int n = arr1.size();
vector<pair<int, int>> bridges;
// Store all bridges
for (int i = 0; i < n; i++)
{
bridges.push_back({arr1[i], arr2[i]});
}
// Sort bridges by first bank and then by second bank
sort(bridges.begin(), bridges.end());
vector<int> dp(n, 1);
int res = 1;
// Find Longest Non-Decreasing Subsequence
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (bridges[j].second <= bridges[i].second)
{
dp[i] = max(dp[i], dp[j] + 1);
}
}
res = max(res, dp[i]);
}
return res;
}
int main()
{
vector<int> arr1 = {3, 1, 4, 4};
vector<int> arr2 = {1, 3, 2, 1};
cout << maxBridges(arr1, arr2);
return 0;
}
import java.util.Arrays;
class GFG {
static int maxBridges(int[] arr1, int[] arr2)
{
int n = arr1.length;
int[][] bridges = new int[n][2];
// Store all bridges
for (int i = 0; i < n; i++) {
bridges[i][0] = arr1[i];
bridges[i][1] = arr2[i];
}
// Sort bridges by first bank and then by second
// bank
Arrays.sort(bridges, (a, b) -> {
if (a[0] != b[0]) {
return Integer.compare(a[0], b[0]);
}
return Integer.compare(a[1], b[1]);
});
int[] dp = new int[n];
Arrays.fill(dp, 1);
int res = 1;
// Find Longest Non-Decreasing Subsequence
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (bridges[j][1] <= bridges[i][1]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
public static void main(String[] args)
{
int[] arr1 = { 3, 1, 4, 4 };
int[] arr2 = { 1, 3, 2, 1 };
System.out.println(maxBridges(arr1, arr2));
}
}
def maxBridges(arr1, arr2):
n = len(arr1)
bridges = []
# Store all bridges
for i in range(n):
bridges.append((arr1[i], arr2[i]))
# Sort bridges by first bank and then by second bank
bridges.sort()
dp = [1] * n
res = 1
# Find Longest Non-Decreasing Subsequence
for i in range(1, n):
for j in range(i):
if bridges[j][1] <= bridges[i][1]:
dp[i] = max(dp[i], dp[j] + 1)
res = max(res, dp[i])
return res
if __name__ == "__main__":
arr1 = [3, 1, 4, 4]
arr2 = [1, 3, 2, 1]
print(maxBridges(arr1, arr2))
using System;
using System.Collections.Generic;
class GFG {
public int maxBridges(int[] arr1, int[] arr2)
{
int n = arr1.Length;
List<(int, int)> bridges = new List<(int, int)>();
// Store all bridges
for (int i = 0; i < n; i++) {
bridges.Add((arr1[i], arr2[i]));
}
// Sort bridges by first bank and then by second
// bank
bridges.Sort();
int[] dp = new int[n];
Array.Fill(dp, 1);
int res = 1;
// Find Longest Non-Decreasing Subsequence
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (bridges[j].Item2 <= bridges[i].Item2) {
dp[i] = Math.Max(dp[i], dp[j] + 1);
}
}
res = Math.Max(res, dp[i]);
}
return res;
}
static void Main()
{
int[] arr1 = { 3, 1, 4, 4 };
int[] arr2 = { 1, 3, 2, 1 };
GFG obj = new GFG();
Console.WriteLine(obj.maxBridges(arr1, arr2));
}
}
function maxBridges(arr1, arr2)
{
let n = arr1.length;
let bridges = [];
// Store all bridges
for (let i = 0; i < n; i++) {
bridges.push([ arr1[i], arr2[i] ]);
}
// Sort bridges by first bank and then by second bank
bridges.sort((a, b) => {
if (a[0] !== b[0]) {
return a[0] - b[0];
}
return a[1] - b[1];
});
let dp = new Array(n).fill(1);
let res = 1;
// Find Longest Non-Decreasing Subsequence
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
if (bridges[j][1] <= bridges[i][1]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
// Driver code
let arr1 = [ 3, 1, 4, 4 ];
let arr2 = [ 1, 3, 2, 1 ];
console.log(maxBridges(arr1, arr2));
Output
3
[Expected Approach] Using Sorting with Binary Search (LNDS) - O(n log n) Time and O(n) Space
The idea is to first sort all the bridges by their first endpoints, and by their second endpoints in case of a tie. After sorting, any valid set of non-crossing bridges must have their second endpoints in non-decreasing order. Thus, the problem reduces to finding the Longest Non-Decreasing Subsequence (LNDS) of the second bank endpoints. The LNDS is computed efficiently using binary search (upper_bound()), and its length gives the maximum number of non-crossing bridges.
Let us understand with an example:
Input: arr1[] = [3, 1, 4, 4], arr2[] = [1, 3, 2, 1]
- Form the bridge pairs: (3,1), (1,3), (4,2), (4,1).
- Sort the pairs by the first endpoint (and second endpoint if tied) : (1,3), (3,1), (4,1), (4,2).
- Consider the second endpoints: 3, 1, 1, 2.
- Build the Longest Non-Decreasing Subsequence (LNDS) using binary search: [] -> [3] -> [1] -> [1,1] -> [1,1,2].
- The length of the LNDS is 3, so the maximum number of non-crossing bridges is 3.
#include <iostream>
#include <vector>
using namespace std;
int maxBridges(vector<int> &arr1, vector<int> &arr2)
{
int n = arr1.size();
vector<pair<int, int>> bridges;
for (int i = 0; i < n; i++)
{
bridges.push_back({arr1[i], arr2[i]});
}
// Sort bridges by first bank and then by second bank
sort(bridges.begin(), bridges.end());
vector<int> res;
for (auto &bridge : bridges)
{
int val = bridge.second;
// Find position for LNDS using binary search
auto idx = upper_bound(res.begin(), res.end(), val);
if (idx == res.end())
{
res.push_back(val);
}
else
{
*idx = val;
}
}
// Length of LNDS gives maximum non-crossing bridges
return res.size();
}
int main()
{
vector<int> arr1 = {3, 1, 4, 4};
vector<int> arr2 = {1, 3, 2, 1};
cout << maxBridges(arr1, arr2);
return 0;
}
import java.util.Arrays;
import java.util.ArrayList;
class GFG {
public int maxBridges(int[] arr1, int[] arr2)
{
int n = arr1.length;
int[][] bridges = new int[n][2];
// Store corresponding bridge endpoints
for (int i = 0; i < n; i++) {
bridges[i][0] = arr1[i];
bridges[i][1] = arr2[i];
}
// Sort bridges by first bank and then by second
// bank
Arrays.sort(bridges, (a, b) -> {
if (a[0] != b[0]) {
return Integer.compare(a[0], b[0]);
}
return Integer.compare(a[1], b[1]);
});
ArrayList<Integer> res = new ArrayList<>();
for (int[] bridge : bridges) {
int val = bridge[1];
// Find position for LNDS using binary search
int idx = upperBound(res, val);
if (idx == res.size()) {
res.add(val);
}
else {
res.set(idx, val);
}
}
// Length of LNDS gives maximum non-crossing
// bridges
return res.size();
}
private int upperBound(ArrayList<Integer> res, int val)
{
int left = 0, right = res.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (res.get(mid) <= val) {
left = mid + 1;
}
else {
right = mid;
}
}
return left;
}
public static void main(String[] args)
{
int[] arr1 = { 3, 1, 4, 4 };
int[] arr2 = { 1, 3, 2, 1 };
GFG obj = new GFG();
System.out.println(obj.maxBridges(arr1, arr2));
}
}
from bisect import bisect_right
def maxBridges(arr1, arr2):
n = len(arr1)
bridges = []
# Store corresponding bridge endpoints.
for i in range(n):
bridges.append((arr1[i], arr2[i]))
# Sort bridges by first bank and then by second bank.
bridges.sort()
res = []
for bridge in bridges:
val = bridge[1]
# Find position for LNDS using binary search.
idx = bisect_right(res, val)
if idx == len(res):
res.append(val)
else:
res[idx] = val
# Length of LNDS gives maximum non-crossing bridges.
return len(res)
if __name__ == "__main__":
arr1 = [6, 4, 2, 1, 2]
arr2 = [2, 3, 6, 5, 4]
print(maxBridges(arr1, arr2))
using System;
using System.Collections.Generic;
class GFG {
public int maxBridges(int[] arr1, int[] arr2)
{
int n = arr1.Length;
List<(int, int)> bridges = new List<(int, int)>();
// Store corresponding bridge endpoints.
for (int i = 0; i < n; i++) {
bridges.Add((arr1[i], arr2[i]));
}
// Sort bridges by first bank and then by second
// bank.
bridges.Sort();
List<int> res = new List<int>();
foreach(var bridge in bridges)
{
int val = bridge.Item2;
// Find position for LNDS using binary search.
int idx = UpperBound(res, val);
if (idx == res.Count) {
res.Add(val);
}
else {
res[idx] = val;
}
}
// Length of LNDS gives maximum non-crossing
// bridges.
return res.Count;
}
private int UpperBound(List<int> res, int val)
{
int left = 0;
int right = res.Count;
while (left < right) {
int mid = left + (right - left) / 2;
if (res[mid] <= val) {
left = mid + 1;
}
else {
right = mid;
}
}
return left;
}
static void Main()
{
int[] arr1 = { 3, 1, 4, 4 };
int[] arr2 = { 1, 3, 2, 1 };
GFG obj = new GFG();
Console.WriteLine(obj.maxBridges(arr1, arr2));
}
}
function maxBridges(arr1, arr2)
{
const n = arr1.length;
let bridges = [];
for (let i = 0; i < n; i++) {
bridges.push([ arr1[i], arr2[i] ]);
}
// Sort bridges by first bank and then by second bank.
bridges.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
let res = [];
for (const bridge of bridges) {
const val = bridge[1];
// Find position for LNDS using binary search.
let idx = res.findIndex(x => x > val);
if (idx === -1) {
res.push(val);
}
else {
res[idx] = val;
}
}
// Length of LNDS gives maximum non-crossing bridges.
return res.length;
}
// Driver Code
const arr1 = [ 3, 1, 4, 4 ];
const arr2 = [ 1, 3, 2, 1 ];
console.log(maxBridges(arr1, arr2));
Output
3