1. Recursion with Memoization / DFS on DAG
Intuition
Model each envelope as a node in a directed acyclic graph (DAG). Add a directed edge from envelope i to envelope j if w_i < w_j and h_i < h_j. Finding the maximum number of nested envelopes is equivalent to finding the length of the longest path in this DAG. Using depth-first search with memoization computes the answer, but constructing the graph or finding all paths takes O(n2) time and substantial recursion overhead, leading to Time Limit Exceeded when n = 105.
When to apply: Small constraint sets (N <= 1000).
Algorithm
- Step 1: Sort envelopes by width to guarantee an acyclic order.
- Step 2: Define a recursive function dfs(idx) returning the maximum chain ending or starting at envelope idx.
- Step 3: In dfs(idx), iterate through all j > idx. If envelopes[j] strictly contains envelopes[idx], recurse on j.
- Step 4: Memoize results in a memo array of size n.
- Step 5: Return 1 + maximum path length over all starting envelopes.
#include <iostream>#include <vector>#include <algorithm> int dfs(int idx, const std::vector<std::vector<int>>& env, std::vector<int>& memo) { if (memo[idx] != -1) return memo[idx]; int maxChain = 1; for (size_t next = idx + 1; next < env.size(); ++next) { if (env[next][0] > env[idx][0] && env[next][1] > env[idx][1]) { maxChain = std::max(maxChain, 1 + dfs(next, env, memo)); } } return memo[idx] = maxChain;} int maxEnvelopesBrute(std::vector<std::vector<int>>& envelopes) { int n = envelopes.size(); if (n == 0) return 0; std::sort(envelopes.begin(), envelopes.end()); std::vector<int> memo(n, -1); int ans = 1; for (int i = 0; i < n; ++i) { ans = std::max(ans, dfs(i, envelopes, memo)); } return ans;} int main() { std::vector<std::vector<int>> envelopes = {{5, 4}, {6, 4}, {6, 7}, {2, 3}}; std::cout << maxEnvelopesBrute(envelopes) << std::endl; return 0;}Time & Space Complexity
- Time complexity:
O(n^2) where n is the number of envelopes - Space complexity:
O(n) auxiliary space for memoization table and call stack