2389.和有限的最长子序列

前言

题目来自LeetCode:https://leetcode.cn/problems/longest-subsequence-with-limited-sum/

题目(难度:简单)

给你一个长度为 n 的整数数组 nums ,和一个长度为 m 的整数数组 queries 。
返回一个长度为 m 的数组 answer ,其中 answer[i] 是 nums 中 元素之和小于等于 queries[i] 的 子序列最大 长度  。
子序列 是由一个数组删除某些元素(也可以不删除)但不改变剩余元素顺序得到的一个数组。
示例1

1
2
3
4
5
6
输入:nums = [4,5,2,1], queries = [3,10,21]
输出:[2,3,4]
解释:queries 对应的 answer 如下:
- 子序列 [2,1] 的和小于或等于 3 。可以证明满足题目要求的子序列的最大长度是 2 ,所以 answer[0] = 2
- 子序列 [4,5,1] 的和小于或等于 10 。可以证明满足题目要求的子序列的最大长度是 3 ,所以 answer[1] = 3
- 子序列 [4,5,2,1] 的和小于或等于 21 。可以证明满足题目要求的子序列的最大长度是 4 ,所以 answer[2] = 4

提示:

  • n == nums.length
  • m == queries.length
  • 1 <= n, m <= 1000
  • 1 <= nums[i], queries[i] <= 10^6

题解

本题关键在读懂题目,题目要求子序列的和,根据子序列的定义,那么原数组的顺序就不重要了,要求小于等于queries[i]的最长子序列,那么只需要从小往大的挑,直到和大于queries[i],那么前面所挑的个数即为答案。
可以通过求前缀和(前缀数组第i项代表原数组前i项的和)解决重复求和的过程,也就是前缀数组第i项代表长度为i的子序列和最小的值。
因为本题1 <= n, m <= 1000,直接在前缀数组上寻找的时间复杂度为O(nm),也不会超出时间限制,若nm > 10^9次方时,那么就需要使用二分查找在前缀数组上查找,此时的时间复杂度为O(mlog(n)).

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Solution {
public int[] answerQueries(int[] nums, int[] queries) {
//将原数组从小到大排序
Arrays.sort(nums);
int nLen = nums.length;
int qLen = queries.length;
//数组nums的前缀和数组pre
int[] pre = new int[nLen];
pre[0] = nums[0];
//求前缀数组的过程
for(int i = 1; i < nLen; i++){
pre[i] = pre[i - 1] + nums[i];
}
int[] res = new int[qLen];
for(int i = 0; i < qLen; i++){
//直接查找
//找出大于queries[i]的pre[j],若所有数加起来也没有queries[i]大,那么原数组全部选上即为答案
int j = 0;
for(; j < nLen; j++){
if(pre[j] > queries[i]){
break;
}
}
res[i] = j;

/*
二分查找
int l = 0;
int r = nLen - 1;
while(l <= r){
int mid = l + (r - l) / 2;
if(pre[mid] > queries[i]){
r = mid - 1;
}else{
l = mid + 1;
}
}
res[i] = l;
*/

}
return res;
}
}