[周赛308: 前缀和、贪心+排序] 6160. 和有限的最长子序列

109 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第30天,点击查看活动详情

每日刷题 2022.08.28

题目

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

示例

  • 示例1
输入: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
  • 示例2
输入:nums = [2,3,4,5], queries = [1]
输出:[0]
解释:空子序列是唯一一个满足元素和小于或等于 1 的子序列,所以 answer[0] = 0

提示

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

解题思路

  • 根据题意分析可知:对于queries数组中的每一个元素(记为:ele),都需要在nums数组中查找到对应的元素之和小于等于ele的最长子序列。
  • 这里子序列的定义:不一定非要是连续的,可以是删除某些元素或者不删除之后形成的子序列。
  • 坑点:思想上的困境,最长子序列的定义是可以不连续,并非不能给nums数组中的元素进行排序。因为将nums数组排序后,连续选择的元素,也可以在元素中依次选择。
  • 因为题目要求的是最长子序列,那么我们将nums数组中的元素从小到大进行排序后,就可以贪心的从前往后依次选择,尽可能地多选择元素值小的,这样子序列就会更长。
  • 对于排序后的nums数组,求前缀和(记为:pre数组)。例如:pre[i] = nums[i - 1] + pre[i - 1],前缀和数组当前的i位置的元素和表示:从当前元素到数组头部的所有元素之和(包含当前元素)。
  • 最后根据queries中的每一个元素去二分查找,pre数组中小于等于的下标,就是当前可以形成饿最长子序列。

AC代码

var answerQueries = function(nums, q) {
  let n = nums.length, m = q.length;
  nums.sort((a, b) => a - b);
  let pre = new Array(n + 1).fill(0);
  for(let i = 1; i <= n; i++) {
    pre[i] = nums[i - 1] + pre[i - 1];
  }
  // console.log(pre)
  let ans = [];
  for(let i = 0; i < m; i++) {
    ans.push(binary(q[i]));
  }
  return ans;
  function binary(tar) {
    let l = 0, r = n + 2;
    while(l + 1 != r) {
      let mid = (l + r) >> 1;
      if(pre[mid] <= tar) {
        l = mid;
      }else {
        r = mid;
      }
    }
    return l;
  }
};