Skip to content

Commit 0a09f99

Browse files
authored
Create README.md
1 parent b69f325 commit 0a09f99

File tree

1 file changed

+280
-0
lines changed
  • 24 - Dynamic Programming Problems/20 - Russian Doll Envelopes

1 file changed

+280
-0
lines changed
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
<h1 align="center">Russian - Doll - Envelopes</h1>
2+
3+
## Problem Statement
4+
5+
**Problem URL :** [Russian Doll Envelopes](https://leetcode.com/problems/russian-doll-envelopes/description/)
6+
7+
![image](https://github.com/user-attachments/assets/699fe0c5-3818-4335-9226-6f76ad046a3a)
8+
9+
### Problem Explanation
10+
The **Russian Doll Envelopes** problem involves a set of envelopes where each envelope has a width and a height. The goal is to find the maximum number of envelopes that can be nested inside one another, following these rules:
11+
12+
1. An envelope can only be nested inside another envelope if both its width and height are strictly smaller than the other envelope.
13+
2. The envelopes must be ordered by increasing width and height for nesting.
14+
15+
### Example:
16+
17+
#### Input:
18+
```cpp
19+
vector<vector<int>> envelopes = {{5,4}, {6,4}, {6,7}, {2,3}};
20+
```
21+
22+
#### Step-by-Step Explanation:
23+
24+
1. **Initial Input**: You are given four envelopes with the following dimensions:
25+
- Envelope 1: (5, 4)
26+
- Envelope 2: (6, 4)
27+
- Envelope 3: (6, 7)
28+
- Envelope 4: (2, 3)
29+
30+
2. **Sorting**: Sort the envelopes by width in ascending order. If two envelopes have the same width, sort them by height in descending order.
31+
32+
After sorting:
33+
```cpp
34+
envelopes = {{2, 3}, {5, 4}, {6, 7}, {6, 4}};
35+
```
36+
- First by width: 2 < 5 < 6 < 6
37+
- If widths are the same, sort by height in descending order (e.g., (6,7) comes before (6,4)).
38+
39+
3. **Finding the Longest Increasing Subsequence (LIS)**:
40+
- Extract the heights from the sorted envelopes: `[3, 4, 7, 4]`.
41+
- Now, find the Longest Increasing Subsequence (LIS) of heights. The LIS of heights `[3, 4, 7, 4]` is `[3, 4, 7]`, which has a length of 3.
42+
- The maximum number of envelopes that can be nested is 3: (2, 3) -> (5, 4) -> (6, 7).
43+
44+
#### Final Output:
45+
```cpp
46+
3
47+
```
48+
49+
### Approach of the Given Code
50+
51+
1. **Sorting**:
52+
- The envelopes are first sorted by width in ascending order. If two envelopes have the same width, they are sorted by height in descending order. This sorting step ensures that we do not need to worry about nested envelopes having the same width, as we handle this case by sorting heights in descending order when widths are equal.
53+
54+
2. **Finding LIS**:
55+
- After sorting, we extract the heights of the envelopes and use a binary search technique (`lower_bound`) to find the LIS of the heights. This approach ensures that we always maintain the smallest possible elements in the LIS, allowing us to extend the sequence with future elements.
56+
57+
3. **Result**:
58+
- The length of the LIS of heights is the maximum number of envelopes that can be nested.
59+
60+
## Problem Solution
61+
```cpp
62+
class Solution {
63+
public:
64+
// Helper function to find the length of the Longest Increasing Subsequence (LIS)
65+
int solve(vector<int>& heights){
66+
int n = heights.size(); // Get the number of envelopes
67+
vector<int> ans; // Vector to store the LIS of heights (not the actual subsequence, just its length)
68+
69+
// Iterate through all the heights to find the LIS
70+
for(int i = 0; i < n; i++){
71+
// If the ans vector is empty or the current height is greater than the last element of ans,
72+
// add the current height to the LIS.
73+
if(ans.empty() || heights[i] > ans.back()){
74+
ans.push_back(heights[i]);
75+
} else {
76+
// Otherwise, find the first element in ans that is greater than or equal to heights[i]
77+
// and replace it with the current height.
78+
// This maintains the smallest possible value for the LIS subsequence, ensuring it's extensible.
79+
int index = lower_bound(ans.begin(), ans.end(), heights[i]) - ans.begin();
80+
ans[index] = heights[i]; // Update the value at the found position
81+
}
82+
}
83+
84+
// Return the size of the LIS (i.e., the maximum number of envelopes that can be nested)
85+
return ans.size();
86+
}
87+
88+
// Main function to solve the Russian Doll Envelopes problem
89+
int maxEnvelopes(vector<vector<int>>& envelopes) {
90+
// Step 1: Sort the envelopes
91+
// Sort first by width in ascending order, and if the widths are the same, by height in descending order
92+
sort(envelopes.begin(), envelopes.end(), [](vector<int>& a, vector<int>& b){
93+
// If the widths are equal, we sort by height in descending order
94+
if(a[0] == b[0]) return a[1] > b[1];
95+
// Otherwise, sort by width in ascending order
96+
return a[0] < b[0];
97+
});
98+
99+
// Step 2: Extract the heights of the envelopes after sorting
100+
vector<int> heights;
101+
for(const auto& envelope : envelopes) heights.push_back(envelope[1]);
102+
103+
// Step 3: Find the LIS of heights (this will give the maximum number of envelopes that can be nested)
104+
return solve(heights);
105+
}
106+
};
107+
108+
```
109+
110+
## Problem Solution Explanation
111+
112+
Let’s break down the given code line by line and explain it in detail with examples.
113+
114+
```cpp
115+
class Solution {
116+
public:
117+
// Helper function to find the length of the Longest Increasing Subsequence (LIS)
118+
int solve(vector<int>& heights){
119+
int n = heights.size(); // Get the number of envelopes
120+
```
121+
122+
- **Line 1**: We begin by declaring the `Solution` class, which contains our solution.
123+
- **Line 2**: The `solve` function is a helper function that takes a vector of integers `heights` and returns the length of the Longest Increasing Subsequence (LIS) of heights.
124+
- **Line 3**: We define `n`, the size of the `heights` vector, which represents the number of envelopes after sorting them by width.
125+
126+
**Example**: Suppose `heights = [3, 4, 7, 4]` (extracted from the envelopes after sorting them by width).
127+
128+
```cpp
129+
vector<int> ans; // Vector to store the LIS of heights (not the actual subsequence, just its length)
130+
```
131+
132+
- **Line 4**: `ans` is a vector that will store the smallest possible elements of the LIS (the length of the LIS is stored, not the actual subsequence).
133+
- **Explanation**: By maintaining the smallest elements, we ensure that we have the best potential to extend the LIS by adding larger elements.
134+
135+
**Example**: `ans = []` (Initially empty).
136+
137+
```cpp
138+
// Iterate through all the heights to find the LIS
139+
for(int i = 0; i < n; i++){
140+
```
141+
142+
- **Line 5**: A loop is started to iterate over all the heights. The goal is to update the LIS progressively.
143+
144+
**Example**: The loop will iterate through `heights = [3, 4, 7, 4]`.
145+
146+
```cpp
147+
// If the ans vector is empty or the current height is greater than the last element of ans,
148+
// add the current height to the LIS.
149+
if(ans.empty() || heights[i] > ans.back()){
150+
```
151+
152+
- **Line 6-7**: If `ans` is empty or if the current height is greater than the last element in `ans` (i.e., `heights[i] > ans.back()`), then the current height should be added to `ans`. This ensures that we are forming an increasing subsequence.
153+
154+
**Example**:
155+
- First iteration: `heights[i] = 3`. `ans = []` (empty), so `ans.push_back(3)`.
156+
- After the first iteration, `ans = [3]`.
157+
158+
```cpp
159+
ans.push_back(heights[i]);
160+
} else {
161+
```
162+
163+
- **Line 8**: If the condition is true, `heights[i]` is added to the `ans` vector.
164+
165+
```cpp
166+
// Otherwise, find the first element in ans that is greater than or equal to heights[i]
167+
// and replace it with the current height.
168+
// This maintains the smallest possible value for the LIS subsequence, ensuring it's extensible.
169+
int index = lower_bound(ans.begin(), ans.end(), heights[i]) - ans.begin();
170+
```
171+
172+
- **Line 9-10**: If the current height is **not greater** than the last element of `ans`, we need to find the first element in `ans` that is greater than or equal to `heights[i]`. We use `lower_bound` to find the position of the first element in `ans` that is greater than or equal to `heights[i]`. This index is then used to update the element at that position with `heights[i]`.
173+
- **Reason**: Replacing the element at `index` ensures that we maintain the smallest possible values in `ans`, which allows for a potentially longer subsequence in future iterations.
174+
175+
**Example**:
176+
- Second iteration: `heights[i] = 4`. The `ans = [3]`, and `4` is greater than `3`, so `ans.push_back(4)`.
177+
178+
- Third iteration: `heights[i] = 7`. The `ans = [3, 4]`, and `7` is greater than `4`, so `ans.push_back(7)`.
179+
180+
- Fourth iteration: `heights[i] = 4`. The `ans = [3, 4, 7]`. Using `lower_bound`, we find that `4` can replace the second element in `ans` (because `4 <= 7`), so we update `ans = [3, 4, 7]`.
181+
182+
```cpp
183+
ans[index] = heights[i]; // Update the value at the found position
184+
}
185+
}
186+
```
187+
188+
- **Line 11**: We replace the element at `index` in `ans` with the current `height[i]`. This maintains the smallest possible value for the LIS subsequence.
189+
190+
```cpp
191+
// Return the size of the LIS (i.e., the maximum number of envelopes that can be nested)
192+
return ans.size();
193+
}
194+
```
195+
196+
- **Line 12-13**: The size of the `ans` vector is returned, which represents the length of the longest increasing subsequence (LIS) of heights. This is the maximum number of envelopes that can be nested.
197+
198+
**Example**: After iterating over all the heights, `ans = [3, 4, 7]`. The length of the LIS is `3`, which means the maximum number of envelopes that can be nested is 3.
199+
200+
### Main Function: `maxEnvelopes`
201+
202+
```cpp
203+
// Main function to solve the Russian Doll Envelopes problem
204+
int maxEnvelopes(vector<vector<int>>& envelopes) {
205+
```
206+
207+
- **Line 14**: The main function `maxEnvelopes` receives a 2D vector `envelopes` where each element represents an envelope with its width and height.
208+
209+
```cpp
210+
// Step 1: Sort the envelopes
211+
// Sort first by width in ascending order, and if the widths are the same, by height in descending order
212+
sort(envelopes.begin(), envelopes.end(), [](vector<int>& a, vector<int>& b){
213+
```
214+
215+
- **Line 15-16**: The envelopes are sorted by their width in ascending order. If two envelopes have the same width, they are sorted by height in descending order. This step ensures that we never have to worry about envelopes with the same width in our LIS computation, as we already handle that scenario by sorting heights in descending order when widths are equal.
216+
217+
**Example**:
218+
- Before sorting: `envelopes = [[5, 4], [6, 4], [6, 7], [2, 3]]`
219+
- After sorting: `envelopes = [[2, 3], [5, 4], [6, 7], [6, 4]]`
220+
221+
```cpp
222+
// If the widths are equal, we sort by height in descending order
223+
if(a[0] == b[0]) return a[1] > b[1];
224+
// Otherwise, sort by width in ascending order
225+
return a[0] < b[0];
226+
});
227+
```
228+
229+
- **Line 17-19**: The lambda function used for sorting:
230+
- If the widths are equal (`a[0] == b[0]`), it sorts by height in descending order (`a[1] > b[1]`).
231+
- Otherwise, it sorts by width in ascending order (`a[0] < b[0]`).
232+
233+
```cpp
234+
// Step 2: Extract the heights of the envelopes after sorting
235+
vector<int> heights;
236+
for(const auto& envelope : envelopes) heights.push_back(envelope[1]);
237+
```
238+
239+
- **Line 20-21**: After sorting, we extract the heights of all the envelopes and store them in a new vector `heights`. This is necessary because the LIS is computed only on the heights.
240+
241+
**Example**: From `envelopes = [[2, 3], [5, 4], [6, 7], [6, 4]]`, we extract `heights = [3, 4, 7, 4]`.
242+
243+
```cpp
244+
// Step 3: Find the LIS of heights (this will give the maximum number of envelopes that can be nested)
245+
return solve(heights);
246+
}
247+
};
248+
```
249+
250+
- **Line 22-23**: Finally, we pass the `heights` vector to the `solve` function, which calculates the length of the Longest Increasing Subsequence of heights and returns the result.
251+
252+
### Example Input and Output:
253+
254+
**Input**:
255+
```cpp
256+
vector<vector<int>> envelopes = {{5,4}, {6,4}, {6,7}, {2,3}};
257+
Solution solution;
258+
cout << solution.maxEnvelopes(envelopes);
259+
```
260+
261+
**Output**:
262+
```cpp
263+
3
264+
```
265+
266+
**Explanation**:
267+
- The sorted envelopes are `[[2, 3], [5, 4], [6, 7], [6, 4]]`.
268+
- The heights are `[3, 4, 7, 4]`.
269+
- The longest increasing subsequence (LIS) of heights is `[3, 4, 7]`, so the maximum number of envelopes that can be nested is `3`.
270+
271+
### Time and Space Complexity:
272+
273+
**Time Complexity**:
274+
- Sorting the envelopes takes `O(n log n)` where `n` is the number of envelopes.
275+
- Finding the LIS using `lower_bound` also takes `O(n log n)` since for each height we perform a binary search in `ans`.
276+
- Overall, the time complexity is `O(n log n)`.
277+
278+
**Space Complexity**:
279+
- We use `O(n)` space to store the heights and the `ans` vector.
280+
- The space complexity is `O(n)`.

0 commit comments

Comments
 (0)