LeetCode 第 222 场周赛

1710.卡车上的最大单元数

题目链接:1710.卡车上的最大单元数

请你将一些箱子装在 一辆卡车 上。给你一个二维数组 boxTypes ,其中 boxTypes[i] = [numberOfBoxesi, numberOfUnitsPerBoxi]

  • numberOfBoxesi 是类型 i 的箱子的数量。
  • numberOfUnitsPerBoxi 是类型 i 每个箱子可以装载的单元数量。

整数 truckSize 表示卡车上可以装载 箱子最大数量 。只要箱子数量不超过 truckSize
,你就可以选择任意箱子装到卡车上。

返回卡车可以装载 单元最大 总数

示例 Sample

示例 1:

**输入:** boxTypes = [[1,3],[2,2],[3,1]], truckSize = 4
**输出:** 8
**解释:** 箱子的情况如下:
- 1 个第一类的箱子,里面含 3 个单元。
- 2 个第二类的箱子,每个里面含 2 个单元。
- 3 个第三类的箱子,每个里面含 1 个单元。
可以选择第一类和第二类的所有箱子,以及第三类的一个箱子。
单元总数 = (1 * 3) + (2 * 2) + (1 * 1) = 8

示例 2:

**输入:** boxTypes = [[5,10],[2,5],[4,7],[3,9]], truckSize = 10
**输出:** 91

提示:

  • 1 <= boxTypes.length <= 1000
  • 1 <= numberOfBoxesi, numberOfUnitsPerBoxi <= 1000
  • 1 <= truckSize <= 106

我的题解

排序贪心即可。

class Solution {
 public:
  int maximumUnits(vector<vector<int>>& boxTypes, int truckSize) {
    sort(boxTypes.begin(), boxTypes.end(), [&](auto a, auto b) { return a[1] > b[1]; });
    int ans(0);
    for (int i = 0; i < boxTypes.size(); i++) {
      int x = min(boxTypes[i][0], truckSize);
      ans += boxTypes[i][1] * x;
      truckSize -= x;
    }
    return ans;
  }
};

1711.大餐计数

题目链接:1711.大餐计数

大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 2 的幂。

你可以搭配 任意 两道餐品做一顿大餐。

给你一个整数数组 deliciousness ,其中 deliciousness[i] 是第 i​​​​​​​​​​​​​​
道餐品的美味程度,返回你可以用数组中的餐品做出的不同 大餐 的数量。结果需要对 109 + 7 取余。

注意,只要餐品下标不同,就可以认为是不同的餐品,即便它们的美味程度相同。

示例 Sample

示例 1:

**输入:** deliciousness = [1,3,5,7,9]
**输出:** 4
**解释:** 大餐的美味程度组合为 (1,3) 、(1,7) 、(3,5) 和 (7,9) 。
它们各自的美味程度之和分别为 4 、8 、8 和 16 ,都是 2 的幂。

示例 2:

**输入:** deliciousness = [1,1,1,3,3,3,7]
**输出:** 15
**解释:** 大餐的美味程度组合为 3 种 (1,1) ,9 种 (1,3) ,和 3 种 (1,7) 。

提示:

  • 1 <= deliciousness.length <= 105
  • 0 <= deliciousness[i] <= 220

我的题解

有卡常 ……

class Solution {
 public:
  int countPairs(vector<int>& deliciousness) {
    const int maxn = (1 << 21) + 7;
    int g[maxn] = {0};
    int ans(0);
    const int MOD = 1e9 + 7;
    const int n = deliciousness.size();
    for (int i : deliciousness) g[i]++;
    set<int> s;
    for (int i : deliciousness) s.insert(i);
    for (int i = 1; i <= (1 << 21); (i <<= 1)) {
      for (int a : s) {
        int b = i - a;
        if (a < b)
          ans = (ans + g[a] * g[b]) % MOD;
        else if (a == b)
          ans = (ans + g[a] * (g[b] - 1ll) / 2) % MOD;
      }
    }
    return ans;
  }
};

1712.将数组分成三个子数组的方案数

题目链接:1712.将数组分成三个子数组的方案数

我们称一个分割整数数组的方案是 好的 ,当它满足:

  • 数组被分成三个 非空 连续子数组,从左至右分别命名为 leftmidright
  • left 中元素和小于等于 mid 中元素和,mid 中元素和小于等于 right 中元素和。

给你一个 非负 整数数组 nums ,请你返回 好的 分割 nums 方案数目。由于答案可能会很大,请你将结果对 109 + 7
取余后返回。

示例 Sample

示例 1:

**输入:** nums = [1,1,1]
**输出:** 1
**解释:** 唯一一种好的分割方案是将 nums 分成 [1] [1] [1] 。

示例 2:

**输入:** nums = [1,2,2,2,5,0]
**输出:** 3
**解释:** nums 总共有 3 种好的分割方案:
[1] [2] [2,2,5,0]
[1] [2,2] [2,5,0]
[1,2] [2,2] [5,0]

示例 3:

**输入:** nums = [3,2,1]
**输出:** 0
**解释:** 没有好的分割方案。

提示:

  • 3 <= nums.length <= 105
  • 0 <= nums[i] <= 104

我的题解

二分

class Solution {
 public:
  int waysToSplit(vector<int>& nums) {
    const int n = nums.size();
    vector<int> s(n);
    for (int i = 0; i < n; ++i) {
      s[i] = nums[i] + (i ? s[i - 1] : 0);
    }
    int ans(0);
    const int MOD = 1e9 + 7;
    for (int i = 0; i < n - 2 && s[i] <= s[n - 1] / 3; i++) {
      int l, r;
      l = lower_bound(s.begin(), s.end(), s[i] * 2) - s.begin();
      l = max(i + 1, l);
      r = upper_bound(s.begin(), s.end(), s[i] + (s[n - 1] - s[i]) / 2) - s.begin();
      r = min(n - 1, max(r, i + 1));
      ans = (ans + r - l) % MOD;
    }
    return ans;
  }
};

双指针(Y总太强啦)。只考虑了 left 和 mid 的分隔,没有单调性。但是,考虑 mid 和 right 的分隔时,left 和 mid 的分隔可以双指针。

class Solution {
 public:
  int waysToSplit(vector<int>& nums) {
    int n = nums.size();
    const int mod = 1e9 + 7;
    vector<int> s(n + 1);
    for (int i = 1; i <= n; i++) s[i] = s[i - 1] + nums[i - 1];
    int ans = 0;
    for (int i = 3, j = 2, k = 2; i <= n; i++) {
      while (s[n] - s[i - 1] < s[i - 1] - s[j - 1]) j++;
      while (k + 1 < i && s[i - 1] - s[k] >= s[k]) k++;
      if (j <= k && s[n] - s[i - 1] >= s[i - 1] - s[j - 1] && s[i - 1] - s[k - 1] >= s[k - 1])
        ans = (ans + k - j + 1) % mod;
    }
    return ans;
  }
};

1713.得到子序列的最少操作次数

题目链接:1713.得到子序列的最少操作次数

给你一个数组 target ,包含若干 互不相同 的整数,以及另一个整数数组 arrarr 可能 包含重复元素。

每一次操作中,你可以在 arr 的任意位置插入任一整数。比方说,如果 arr = [1,4,1,2] ,那么你可以在中间添加 3 得到
[1,4, **3** ,1,2] 。你可以在数组最开始或最后面添加整数。

请你返回 最少 操作次数,使得 __target __ 成为 arr 的一个子序列。

一个数组的 子序列 指的是删除原数组的某些元素(可能一个元素都不删除),同时不改变其余元素的相对顺序得到的数组。比方说,[2,7,4]
[4, **2** ,3, **7** ,2,1, **4** ] 的子序列(加粗元素),但 [2,4,2] 不是子序列。

示例 Sample

示例 1:

**输入:** target = [5,1,3], arr = [9,4,2,3,4]
**输出:** 2
**解释:** 你可以添加 5 和 1 ,使得 arr 变为 [ **5** ,9,4, **1** ,2,3,4] ,target 为 arr 的子序列。

示例 2:

**输入:** target = [6,4,8,1,3,2], arr = [4,7,6,2,3,8,6,1]
**输出:** 3

提示:

  • 1 <= target.length, arr.length <= 105
  • 1 <= target[i], arr[i] <= 109
  • target 不包含任何重复元素。

我的题解

满足唯一性时,LCS(最长公共子序列) 转为 LIS (最长上升子序列)。
知识点++。

class Solution {
 public:
  int minOperations(vector<int>& target, vector<int>& arr) {
    unordered_map<int, int> pos;
    for (int i = 0; i < target.size(); i++) pos[target[i]] = i;
    vector<int> a;
    for (int x : arr) {
      if (pos.count(x)) a.emplace_back(pos[x]);
    }
    int len = 0;
    vector<int> q(a.size() + 1);
    for (int i = 0; i < a.size(); i++) {
      int l = 0, r = len, mid;
      while (l < r) {
        mid = l + r + 1 >> 1;
        if (q[mid] < a[i])
          l = mid;
        else
          r = mid - 1;
      }
      len = max(len, r + 1);
      q[r + 1] = a[i];
    }
    return target.size() - len;
  }
};
原文地址:https://www.cnblogs.com/Forgenvueory/p/14232455.html