Maximize block fee with transaction selection
Company: Coinbase
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Onsite
Quick Answer: This question evaluates algorithmic optimization and dependency-aware selection skills, focusing on resource-constrained selection (knapsack-like trade-offs) and graph-based dependency reasoning within the Coding & Algorithms domain.
Part 1: Greedy Block Fee Selection Without Dependencies
Constraints
- 0 <= len(transactions) <= 100000
- Each transaction is (id, size, fee)
- id is a unique positive integer
- 1 <= size <= 10^9
- 1 <= fee <= 10^9
- Block capacity is exactly 100
- Do not use a heap or priority queue
Examples
Input: ([(1, 50, 100), (2, 30, 90), (3, 60, 120), (4, 20, 30)],)
Expected Output: 210
Explanation: Sorted by density with tie-breaks: transaction 2, then 3, then 1, then 4. The greedy policy takes 2 and 3 for total size 90 and total fee 210.
Input: ([],)
Expected Output: 0
Explanation: There are no transactions to select.
Input: ([(10, 101, 1000)],)
Expected Output: 0
Explanation: The only transaction is larger than the block capacity, so it is skipped.
Input: ([(1, 25, 50), (2, 50, 100), (3, 25, 40), (4, 75, 120)],)
Expected Output: 190
Explanation: Transactions 1 and 2 have equal density, so transaction 2 comes first due to higher fee. The greedy policy takes 2, then 1, skips 4 because it does not fit, and takes 3.
Input: ([(1, 80, 160), (2, 30, 90), (3, 70, 140), (4, 20, 30)],)
Expected Output: 230
Explanation: The greedy policy takes transaction 2 first, skips transaction 1 because it no longer fits, then takes transaction 3.
Hints
- Sort by fee per unit size, not by fee alone.
- When comparing fee / size values, cross-multiplication avoids floating-point precision issues.
Part 2: Optimal Block Fee Selection With Parent Dependencies
Constraints
- 0 <= len(transactions) <= 1000
- Each transaction is (id, size, fee, parent_id)
- id is a unique positive integer
- parent_id is -1 or the id of an existing transaction
- Each transaction has at most one parent
- The dependency graph has no cycles
- 1 <= size <= 10^9
- 1 <= fee <= 10^9
- Block capacity is exactly 100
Examples
Input: ([],)
Expected Output: 0
Explanation: There are no transactions.
Input: ([(1, 50, 60, -1), (2, 40, 50, -1), (3, 60, 70, -1)],)
Expected Output: 120
Explanation: There are no dependencies. The best valid subset is transactions 2 and 3, with total size 100 and total fee 120.
Input: ([(1, 40, 10, -1), (2, 30, 100, 1), (3, 30, 100, 2)],)
Expected Output: 210
Explanation: Transaction 3 requires 2, and 2 requires 1. All three fit exactly with total size 100 and total fee 210.
Input: ([(1, 40, 5, -1), (2, 40, 100, 1), (3, 60, 80, -1), (4, 50, 70, -1)],)
Expected Output: 105
Explanation: The best subset is transactions 1 and 2. Transaction 2 has high fee but requires transaction 1, so the combined package has size 80 and fee 105.
Input: ([(1, 20, 20, -1), (2, 50, 80, 1), (3, 50, 90, 1), (4, 60, 95, -1)],)
Expected Output: 115
Explanation: The best subset is transactions 1 and 4, with size 80 and fee 115. Although children 2 and 3 are available after selecting 1, neither can fit together with transaction 4.
Hints
- For each node, compute a small knapsack table for capacities 0 through 100.
- When processing a node, first force the node itself to be selected, then merge in each child subtree. After that, also allow the option of selecting nothing from that subtree.