Optimal Product Selection — Problem Statement & Solution Guide
Quick Answer & Algorithm Key Takeaway
Arrays
O(n)O(1)Problem Description
Given an array of products where each product is represented as a two‑element array [rating, price], and an integer budget, design a function that returns a product whose price does not exceed the budget and whose rating is maximal among all affordable products. If several products share the same highest rating within the budget, any one of them may be returned. The function should run efficiently for large inputs.
DSA Pattern Breakdown
DSA Pattern Breakdown
"Optimal Product Selection"
WHY DOES IT MATTER?
This pattern exemplifies the "single‑pass maximum under constraint" paradigm, which appears in budgeting, resource allocation, and real‑time decision systems where latency and memory are critical.
OPTIMIZATION CHALLENGE
Recognizing that only the best affordable candidate matters allows us to discard sorting entirely and maintain just two scalar trackers, collapsing the problem to O(n) time and O(1) auxiliary space.
REAL-WORLD CONNECTION
Think of a cloud‑cost optimizer that must pick the most performant VM instance that fits within a customer's spend limit—scanning the catalog once yields the optimal choice without costly sorting or external services.
In an interview, initialize bestRating to -Infinity and bestProduct to null, then iterate; this pattern is easy to code, avoids off‑by‑one errors, and demonstrates clear, intentional state management.
COMPLEXITY AT A GLANCE
O(n)O(1)Core Theory — Why This Approach?
The problem is a classic selection under a single constraint: we must choose an element with the maximum secondary attribute (rating) while respecting a primary bound (price ≤ budget). A naïve approach might sort the entire array by rating or price, which incurs O(n log n) time, or even examine every possible subset if the requirement were more complex, leading to exponential blow‑up. However, because each product is independent and we only need the single best affordable item, we can solve it in linear time by a single pass, maintaining the current best candidate. This leverages the optimal substructure property: after processing the first i elements, the best affordable product among them is sufficient to decide the final answer when the (i+1)‑th element is examined. The linear scan thus eliminates unnecessary ordering work and uses constant extra space, which is optimal for this single‑constraint selection problem.
Interview Questions on This Problem
Q1How would you adapt the solution to return all products that share the highest rating within the budget instead of just one?
Perform the same linear scan to find the maximal rating among affordable items, then make a second pass (or collect during the first pass) to gather every product whose price ≤ budget and rating equals that maximal rating, yielding O(n) time and O(k) extra space where k is the number of top products.
Q2If the budget constraint changes dynamically (multiple queries with different budgets), what data structure can answer each query in sub‑linear time?
Sort the products by price and build a prefix‑maximum array of ratings; then for each budget perform a binary search to locate the last affordable index and retrieve the stored maximum rating in O(log n) time per query, with O(n) preprocessing.
Q3Explain how you would handle the case where products can have duplicate ratings but you must return the cheapest among the highest‑rated affordable items.
During the linear scan keep two variables: bestRating and bestPrice. When encountering a product with price ≤ budget, update if rating > bestRating, or if rating == bestRating and price < bestPrice. This ensures the cheapest product among the top‑rated ones is selected, still in O(n) time and O(1) space.
Examples
Input
products = [[5,120],[8,200],[7,150],[9,300]], budget = 180
Output
[7,150]
Explanation: Only products with price ≤180 are considered: [5,120] and [7,150]. Their ratings are 5 and 7 respectively, so the product with rating 7 is chosen.
Input
products = [[4,50],[6,80],[6,70],[3,40]], budget = 75
Output
[6,70]
Explanation: Affordable products are [4,50], [6,70] and [3,40]. The highest rating among them is 6, appearing in two products. Either [6,70] or [6,80] would be valid; the example returns [6,70].
Input
products = [[10,500],[9,400],[8,300]], budget = 250
Output
[]
Explanation: No product costs 250 or less, therefore the function returns an empty array to indicate that no suitable product exists.
Constraints
- 1 <= products.length <= 10^5
- 0 <= rating <= 10^9
- 0 <= price <= 10^9
- 0 <= budget <= 10^9
Optimal Approach & Strategy
Iterate once, updating the best affordable product on the fly, achieving O(n) time and O(1) space.
Brute Force Approach
Sort the array by rating (or price) and then scan to find the first affordable product, which costs O(n log n) time.
Code Solutions
function optimalProductSelection(products, budget) {
let bestRating = -Infinity;
let bestPrice = -1;
for(const [rating, price] of products){
if(price<=budget && rating>bestRating){
bestRating = rating;
bestPrice = price;
}
}
if(bestRating===-Infinity) return [];
return [bestRating, bestPrice];
}
// Driver (same as template)
const readline = require('readline');
const rl = readline.createInterface({input: process.stdin, output: process.stdout});
let lines = [];
rl.on('line', (line) => { lines.push(line.trim()); });
rl.on('close', () => {
let idx = 0;
const n = parseInt(lines[idx++]);
const products = [];
for(let i=0;i<n;i++){
const [rating, price] = lines[idx++].split(/\s+/).map(Number);
products.push([rating, price]);
}
const budget = parseInt(lines[idx++]);
const ans = optimalProductSelection(products, budget);
if(ans.length===0) console.log('[]');
else console.log(`[${ans[0]},${ans[1]}]`);
});#include <bits/stdc++.h>
using namespace std;
vector<int> optimalProductSelection(const vector<vector<int>>& products, int budget) {
int bestRating = -1;
int bestPrice = -1;
for(const auto& p: products){
int rating = p[0];
int price = p[1];
if(price<=budget){
if(rating>bestRating){
bestRating = rating;
bestPrice = price;
}
}
}
if(bestRating==-1) return {};
return {bestRating, bestPrice};
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n; if(!(cin>>n)) return 0;
vector<vector<int>> products(n, vector<int>(2));
for(int i=0;i<n;++i) cin>>products[i][0]>>products[i][1];
int budget; cin>>budget;
vector<int> ans = optimalProductSelection(products, budget);
if(ans.empty()) cout<<"[]\n";
else cout<<'['<<ans[0]<<','<<ans[1]<<"]\n";
return 0;
}import java.io.*;
import java.util.*;
public class Main {
public static List<Integer> optimalProductSelection(List<int[]> products, int budget) {
int bestRating = Integer.MIN_VALUE;
int bestPrice = -1;
for(int[] p : products){
int rating = p[0];
int price = p[1];
if(price <= budget && rating > bestRating){
bestRating = rating;
bestPrice = price;
}
}
if(bestRating == Integer.MIN_VALUE) return new ArrayList<>();
List<Integer> res = new ArrayList<>();
res.add(bestRating);
res.add(bestPrice);
return res;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
if(line == null) return;
int n = Integer.parseInt(line.trim());
List<int[]> products = new ArrayList<>();
for(int i=0;i<n;i++){
String[] parts = br.readLine().trim().split("\\s+");
int rating = Integer.parseInt(parts[0]);
int price = Integer.parseInt(parts[1]);
products.add(new int[]{rating, price});
}
int budget = Integer.parseInt(br.readLine().trim());
List<Integer> ans = optimalProductSelection(products, budget);
if(ans.isEmpty()) System.out.println("[]");
else System.out.println("["+ans.get(0)+","+ans.get(1)+"]");
}
}def optimal_product_selection(products, budget):
best_rating = -1
best_price = -1
for rating, price in products:
if price <= budget and rating > best_rating:
best_rating = rating
best_price = price
if best_rating == -1:
return []
return [best_rating, best_price]
if __name__ == "__main__":
import sys
data = sys.stdin.read().strip().splitlines()
if not data:
sys.exit()
idx = 0
n = int(data[idx]); idx+=1
products = []
for _ in range(n):
rating, price = map(int, data[idx].split()); idx+=1
products.append([rating, price])
budget = int(data[idx])
ans = optimal_product_selection(products, budget)
if not ans:
print('[]')
else:
print(f'[{ans[0]},{ans[1]}]')function optimalProductSelection(products, budget) {
let bestRating = -Infinity;
let bestPrice = -1;
for(const [rating, price] of products){
if(price<=budget && rating>bestRating){
bestRating = rating;
bestPrice = price;
}
}
if(bestRating===-Infinity) return [];
return [bestRating, bestPrice];
}
// Driver (same as template)
const readline = require('readline');
const rl = readline.createInterface({input: process.stdin, output: process.stdout});
let lines = [];
rl.on('line', (line) => { lines.push(line.trim()); });
rl.on('close', () => {
let idx = 0;
const n = parseInt(lines[idx++]);
const products = [];
for(let i=0;i<n;i++){
const [rating, price] = lines[idx++].split(/\s+/).map(Number);
products.push([rating, price]);
}
const budget = parseInt(lines[idx++]);
const ans = optimalProductSelection(products, budget);
if(ans.length===0) console.log('[]');
else console.log(`[${ans[0]},${ans[1]}]`);
});Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.