Longest Subsequence With Limited Sum
Sort, prefix-sum, then upper_bound each query to find how many small items fit under the cap.
4 min read
sorting prefix-sum binary-search greedy
Problem#
For each query, return the maximum length of a subsequence of nums whose sum is at most the query.
Examples#
nums = [4,5,2,1], queries = [3,10,21]→[2,3,4]nums = [2,3,4,5], queries = [1]→[0]
Constraints#
n == nums.length,m == queries.length,1 <= n, m <= 1000,1 <= nums[i] <= 10^6,1 <= queries[i] <= 10^9.
Approach#
Greedy: smaller numbers fit more. Sort nums ascending, build prefix sums, then for each query find the largest prefix length whose sum does not exceed the query via upper_bound.
Solution#
class Solution {public: vector<int> answerQueries(vector<int>& nums, vector<int>& queries) { sort(nums.begin(), nums.end()); int n = nums.size(); vector<long long> pref(n + 1, 0); for (int i = 0; i < n; ++i) pref[i + 1] = pref[i] + nums[i]; vector<int> ans; ans.reserve(queries.size()); for (int q : queries) { int k = upper_bound(pref.begin(), pref.end(), (long long)q) - pref.begin() - 1; ans.push_back(k); } return ans; }};import "sort"
func answerQueries(nums []int, queries []int) []int { sort.Ints(nums) n := len(nums) pref := make([]int, n+1) for i := 0; i < n; i++ { pref[i+1] = pref[i] + nums[i] } ans := make([]int, len(queries)) for i, q := range queries { // first index where pref > q; subtract 1 ans[i] = sort.SearchInts(pref, q+1) - 1 } return ans}from bisect import bisect_rightfrom itertools import accumulatefrom typing import List
class Solution: def answerQueries(self, nums: List[int], queries: List[int]) -> List[int]: nums.sort() pref = [0] + list(accumulate(nums)) ans = [] for q in queries: # bisect_right finds first index where pref > q; subtract 1 ans.append(bisect_right(pref, q) - 1) return ansfunction answerQueries(nums, queries) { nums.sort((a, b) => a - b); const n = nums.length; const pref = new Array(n + 1).fill(0); for (let i = 0; i < n; i++) pref[i + 1] = pref[i] + nums[i]; // upper_bound: first index where pref > q const upperBound = (q) => { let lo = 0, hi = pref.length; while (lo < hi) { const mid = (lo + hi) >> 1; if (pref[mid] <= q) lo = mid + 1; else hi = mid; } return lo; }; return queries.map(q => upperBound(q) - 1);}class Solution { public int[] answerQueries(int[] nums, int[] queries) { Arrays.sort(nums); int n = nums.length; long[] pref = new long[n + 1]; for (int i = 0; i < n; i++) pref[i + 1] = pref[i] + nums[i]; int[] ans = new int[queries.length]; for (int i = 0; i < queries.length; i++) { ans[i] = upperBound(pref, queries[i]) - 1; } return ans; }
private int upperBound(long[] pref, long q) { int lo = 0, hi = pref.length; while (lo < hi) { int mid = (lo + hi) >>> 1; if (pref[mid] <= q) lo = mid + 1; else hi = mid; } return lo; }}function answerQueries(nums: number[], queries: number[]): number[] { nums.sort((a, b) => a - b); const n = nums.length; const pref: number[] = new Array(n + 1).fill(0); for (let i = 0; i < n; i++) pref[i + 1] = pref[i] + nums[i]; // upper_bound: first index where pref > q const upperBound = (q: number): number => { let lo = 0, hi = pref.length; while (lo < hi) { const mid = (lo + hi) >> 1; if (pref[mid] <= q) lo = mid + 1; else hi = mid; } return lo; }; return queries.map(q => upperBound(q) - 1);}Editorial#
Picking the smallest available elements is always optimal for maximizing count. upper_bound on the prefix-sum array returns the first prefix that exceeds the query; subtracting one gives the maximum admissible length.
Complexity#
- Time: O((n + q) log n).
- Space: O(n).
Concept revision#
Related#