[GeeksForGeeks] Maximum Length Chain of Pairs
You are given n pairs of numbers. In every pair, the first number is always smaller than the second number. A pair (c, d) can follow another pair (a, b) if b < c. Chain of pairs can be formed in this fashion. Find the longest chain which can be formed from a given set of pairs.
For example, if the given pairs are {{5, 24}, {39, 60}, {15, 28}, {27, 40}, {50, 90} }, then the longest chain that can be formed is of length 3, and the chain is {{5, 24}, {27, 40}, {50, 90}}
Solution 1. Dynamic Programming, O(n^2) runtime
This problem looks similar with the Longest Increasing Subsequence but not quite the same. In LIS, the sequence's relative order is given and can not be changed.
In this problem, we are only given some pairs of numbers and no enforced order, we can rearrange their relative order to get the longest chain.
Algorithm:
1. sort the given pairs either by their first numbers or second numbers. Doing this reduces this problem to Longest Increasing Subsequence.
2. The LIS problem can be solved using recursion or dynamic programming.
1 import java.util.ArrayList; 2 import java.util.Arrays; 3 import java.util.Comparator; 4 5 class Pair { 6 int first; 7 int second; 8 Pair(int first, int second) { 9 this.first = first; 10 this.second = second; 11 } 12 void display() { 13 System.out.println(this.first + ", " + this.second); 14 } 15 } 16 public class MaxLenChain { 17 private int max = 0; 18 public int getLongestChainRecursion(Pair[] pairs) { 19 if(pairs == null || pairs.length == 0) { 20 return 0; 21 } 22 Comparator<Pair> comp = new Comparator<Pair>(){ 23 public int compare(Pair p1, Pair p2) { 24 return p1.second - p2.second; 25 } 26 }; 27 Arrays.sort(pairs, comp); 28 for(int i = 0; i < pairs.length; i++) { 29 recursionHelper(pairs, i); 30 } 31 return max; 32 } 33 private int recursionHelper(Pair[] pairs, int endIdx) { 34 int len = 1; 35 for(int j = 0; j < endIdx; j++) { 36 if(pairs[j].second < pairs[endIdx].first) { 37 len = Math.max(len, recursionHelper(pairs, j) + 1); 38 } 39 } 40 if(len > max) { 41 max = len; 42 } 43 return len; 44 } 45 public int getLongestChainDp(Pair[] pairs) { 46 Comparator<Pair> comp = new Comparator<Pair>(){ 47 public int compare(Pair p1, Pair p2) { 48 return p1.second - p2.second; 49 } 50 }; 51 Arrays.sort(pairs, comp); 52 ArrayList<Pair> longestChain = new ArrayList<Pair>(); 53 int[] LcEndAt = new int[pairs.length]; 54 int max = 0; int maxIdx = -1; 55 for(int i = 0; i < pairs.length; i++) { 56 LcEndAt[i] = 1; 57 for(int j = 0; j < i; j++) { 58 if(pairs[j].second < pairs[i].first) { 59 LcEndAt[i] = Math.max(LcEndAt[i], LcEndAt[j] + 1); 60 } 61 } 62 if(LcEndAt[i] > max) { 63 max = LcEndAt[i]; 64 maxIdx = i; 65 } 66 } 67 int maxLen = max; 68 while(maxLen > 0) { 69 longestChain.add(pairs[maxIdx]); 70 maxLen--; 71 for(int i = 0; i < maxIdx; i++) { 72 if(LcEndAt[i] == maxLen){ 73 maxIdx = i; 74 break; 75 } 76 } 77 } 78 for(int i = longestChain.size() - 1; i >= 0; i--) { 79 longestChain.get(i).display(); 80 } 81 return max; 82 } 83 public static void main(String[] args) { 84 Pair p1 = new Pair(5,24); 85 Pair p2 = new Pair(39,60); 86 Pair p3 = new Pair(15,28); 87 Pair p4 = new Pair(27,40); 88 Pair p5 = new Pair(50,90); 89 Pair[] pairs = {p1,p2,p3,p4,p5}; 90 /*Pair p1 = new Pair(90,100); 91 Pair p2 = new Pair(70,80); 92 Pair p3 = new Pair(50,60); 93 Pair p4 = new Pair(30,40); 94 Pair[] pairs = {p1,p2,p3,p4};*/ 95 MaxLenChain test = new MaxLenChain(); 96 System.out.println(test.getLongestChainDp(pairs)); 97 } 98 }
Solution 2. Greedy Algorithm(Activity Selection Problem), O(n*logn) runtime
This problem is also a variation of Activity Selection Problem and can be solved in O(n*logn) runtime. To solve it as an activity selection problem,
consider the first element of a pair as start time in activity selection problem, and the second element as end time.
Algorithm:
1. sort the activities according to their finishing time.
2. select the first activity from the sorted array as the first activity in the optimal solution.
3. do the following for remaining activities in the sorted array.
pick the next activity whose finish time is the smallest among the remaining activities and its start time is >= the finish time of previously selected activity.
Proof of correctness
Let the give set of activities be S = {1, 2, 3, ..n} and activities be sorted by finish time. The greedy choice is to always pick activity 1. How come the activity 1 always provides one of the optimal solutions. We can prove it by showing that if there is another solution B with first activity other than 1, then there is also a solution A of same size with activity 1 as first activity. Let the first activity selected by B be k, then there always exist A = {B – {k}} U {1}.(Note that the activities in B are independent and k has smallest finishing time among all. Since k is not 1, finish(k) >= finish(1)).
1 public int getLongestChainGreedy(Pair[] pairs) { 2 Comparator<Pair> comp = new Comparator<Pair>(){ 3 public int compare(Pair p1, Pair p2) { 4 return p1.second - p2.second; 5 } 6 }; 7 Arrays.sort(pairs, comp); 8 int maxLen = 1; int prevPicked = 0, currIdx = 1; 9 while(currIdx < pairs.length) { 10 if(pairs[prevPicked].second <= pairs[currIdx].first) { 11 maxLen++; 12 prevPicked = currIdx; 13 } 14 currIdx++; 15 } 16 return maxLen; 17 }
The same greedy algorithm can also be implemented by sorting for start time and traverse backward. The last activity whose start time is the biggest is always in one of the optimal solution.
This problem is similar but not the same with Number of Airplanes in the sky.
Similarity: both problems are given activities with a start and end time.
Difference: This problem looks for the max result "horizontally", Number of Airplanes in the sky looks for the max result "vertically".
This problems looks for the most activities that does not have time overlap;
Number of Airplanes in the sky looks for at any time, the most number of activities that can happen at the same time.
Related Problems