且听疯吟

leetcode conttest 301

2022-11-01

leetcode conttest 301

周赛的质量终于又好起来了,最后一题难度果真上去了。

6112. 装满杯子需要的最短总时长

题目

现有一台饮水机,可以制备冷水、温水和热水。每秒钟,可以装满 2 杯 不同 类型的水或者 1 杯任意类型的水。

给你一个下标从 0 开始、长度为 3 的整数数组 amount ,其中 amount[0]amount[1]amount[2] 分别表示需要装满冷水、温水和热水的杯子数量。返回装满所有杯子所需的 最少 秒数。

示例 1:

输入:amount = [1,4,2]
输出:4
解释:下面给出一种方案:
第 1 秒:装满一杯冷水和一杯温水。
第 2 秒:装满一杯温水和一杯热水。
第 3 秒:装满一杯温水和一杯热水。
第 4 秒:装满一杯温水。
可以证明最少需要 4 秒才能装满所有杯子。

示例 2:

输入:amount = [5,4,4]
输出:7
解释:下面给出一种方案:
第 1 秒:装满一杯冷水和一杯热水。
第 2 秒:装满一杯冷水和一杯温水。
第 3 秒:装满一杯冷水和一杯温水。
第 4 秒:装满一杯温水和一杯热水。
第 5 秒:装满一杯冷水和一杯热水。
第 6 秒:装满一杯冷水和一杯温水。
第 7 秒:装满一杯热水。

示例 3:

输入:amount = [5,0,0]
输出:5
解释:每秒装满一杯冷水。

提示:

  • amount.length == 3
  • 0 <= amount[i] <= 100

地址

https://leetcode.cn/problems/evaluate-boolean-binary-tree

题意

数学问题 + 贪心匹配

思路

  1. 假设满足 $a \le b \le c$ 条件,分以下两种情况讨论:
  • $a + b \le c$, 此时需要的时间为 $c$ 秒;
  • $a + b > c$,次时需要的时间为 $\lceil \frac{a + b - c}{2}\rceil + c$. 设 $t = a + b -c$,$x = \lfloor \frac{t}{2}\rfloor$,此时满足我们知道一定满足 $a >= x$, 否则就不会满足 $b \le c$ 这个条件了,
    • 此时如果 $t$ 为偶数,我们令 $a = \frac{t}{2} + a - \frac{t}{2}, b = \frac{t}{2} + b - \frac{t}{2}$, 此时需要的时间即为 $c + \frac{t}{2}$。
    • 此时如果 $t$ 为奇数,我们令 $a = x - 1 + (a - x + 1), b = x + b - x$,此时需要的时间即为 $c + x$。
  1. 复杂度分析:
  • 时间复杂度:$O(1)$。
  • 空间复杂度:$O(1)$。

代码

class Solution {
public:
int fillCups(vector<int>& amount) {
sort(amount.begin(), amount.end());
if (amount[0] + amount[1] <= amount[2]) {
return amount[2];
} else {
int rest = amount[0] + amount[1] - amount[2];
return (rest + 1) / 2 + amount[2];
}
}
};

6113. 无限集中的最小数字

题目

现有一个包含所有正整数的集合 [1, 2, 3, 4, 5, ...]

实现 SmallestInfiniteSet 类:

  • SmallestInfiniteSet() 初始化 SmallestInfiniteSet 对象以包含 所有 正整数。
  • int popSmallest() 移除 并返回该无限集中的最小整数。
  • void addBack(int num) 如果正整数 num 不 存在于无限集中,则将一个 num 添加 到该无限集中。

 

示例:

输入
["SmallestInfiniteSet", "addBack", "popSmallest", "popSmallest", "popSmallest", "addBack", "popSmallest", "popSmallest", "popSmallest"]
[[], [2], [], [], [], [1], [], [], []]
输出
[null, null, 1, 2, 3, null, 1, 4, 5]

解释
SmallestInfiniteSet smallestInfiniteSet = new SmallestInfiniteSet();
smallestInfiniteSet.addBack(2); // 2 已经在集合中,所以不做任何变更。
smallestInfiniteSet.popSmallest(); // 返回 1 ,因为 1 是最小的整数,并将其从集合中移除。
smallestInfiniteSet.popSmallest(); // 返回 2 ,并将其从集合中移除。
smallestInfiniteSet.popSmallest(); // 返回 3 ,并将其从集合中移除。
smallestInfiniteSet.addBack(1); // 将 1 添加到该集合中。
smallestInfiniteSet.popSmallest(); // 返回 1 ,因为 1 在上一步中被添加到集合中,
// 且 1 是最小的整数,并将其从集合中移除。
smallestInfiniteSet.popSmallest(); // 返回 4 ,并将其从集合中移除。
smallestInfiniteSet.popSmallest(); // 返回 5 ,并将其从集合中移除。

提示:

  • 1 <= num <= 1000
  • 最多调用 popSmallestaddBack 方法 共计 1000

地址

https://leetcode.cn/problems/smallest-number-in-infinite-set

题意

设计问题

思路

  1. 比较简单的问题,对所有去掉的数用哈希表记录下。
  • popSmallest: 我们从最小的 $1$ 开始增加,直到其不在哈希表存在记录。
  • addBack:我们将 $num$ 从哈希表中删除即可。
  1. 复杂度分析:
  • 时间复杂度:popSmallest 函数的时间复杂度为 $O(n)$,addBack 函数的时间复杂度为 $O(1)$。$n$ 表示函数调用的次数。
  • 空间复杂度:$O(n)$。

代码

class SmallestInfiniteSet {
public:
SmallestInfiniteSet() {

}

int popSmallest() {
int i = 1;
while (remove.count(i)) {
i++;
}
remove.emplace(i);
return i;
}

void addBack(int num) {
if (remove.count(num)) {
remove.erase(num);
}
}
private:
unordered_set<int> remove;
};

/**
* Your SmallestInfiniteSet object will be instantiated and called as such:
* SmallestInfiniteSet* obj = new SmallestInfiniteSet();
* int param_1 = obj->popSmallest();
* obj->addBack(num);
*/

6114. 移动片段得到字符串

题目

给你两个字符串 starttarget ,长度均为n 。每个字符串 仅 由字符 'L'、'R''_' 组成,其中:

字符 'L''R' 表示片段,其中片段 ‘L’ 只有在其左侧直接存在一个 空位 时才能向 左 移动,而片段 ‘R’ 只有在其右侧直接存在一个 空位 时才能向 右 移动。
字符 ‘_’ 表示可以被 任意 ‘L’ 或 ‘R’ 片段占据的空位。
如果在移动字符串 start 中的片段任意次之后可以得到字符串 target ,返回 true ;否则,返回 false

 

示例 1:

输入:start = "_L__R__R_", target = "L______RR"
输出:true
解释:可以从字符串 start 获得 target ,需要进行下面的移动:
- 将第一个片段向左移动一步,字符串现在变为 "L___R__R_" 。
- 将最后一个片段向右移动一步,字符串现在变为 "L___R___R" 。
- 将第二个片段向右移动散步,字符串现在变为 "L______RR" 。
可以从字符串 start 得到 target ,所以返回 true 。

示例 2:

输入:start = "R_L_", target = "__LR"
输出:false
解释:字符串 start 中的 'R' 片段可以向右移动一步得到 "_RL_" 。
但是,在这一步之后,不存在可以移动的片段,所以无法从字符串 start 得到 target 。

示例 3:

输入:start = "_R", target = "R_"
输出:false
解释:字符串 start 中的片段只能向右移动,所以无法从字符串 start 得到 target 。

提示:

  • n == start.length == target.length
  • 1 <= n <= 105
  • starttarget 由字符 'L'、'R''_' 组成

地址

https://leetcode.cn/problems/move-pieces-to-obtain-a-string

题意

模拟

思路

  1. 感觉就是直接模拟,我们依次记录 $start, target$ 中所有出现出现的 L,R 以及其位置,字符即为字符串 $s_1,s_2$, 索引数组记录为 $p_1,p_2$, 二者比较如下:
  • 如果目标字符串的长度不相等,则直接返回 false;
  • 对于第 $i$ 个字符,如果不满足 $s_1[i] = s_2[i]$ 则直接返回错误;
  • 对于第 $i$ 个字符:
    • 如果 $s_1[i] = \texttt{‘L’}$, 此时字符只能向左移动,如果不能满足 $p_1[i] \ge p_2[i]$, 则应该返回错误;
    • 如果 $s_1[i] = \texttt{‘R’}$, 此时字符只能向右移动,如果不能满足 $p_1[i] \le p_2[i]$, 则应该返回错误;
  1. 复杂度分析:
  • 时间复杂度:$O(n)$, $n$ 表示给定的字符串的长度。
  • 空间复杂度:$O(n)$,$n$ 表示给定的字符串的长度。

代码

class Solution {
public:
bool canChange(string start, string target) {
int n = start.size();
vector<pair<char,int>> arr1;
vector<pair<char,int>> arr2;
for (int i = 0; i < n; i++) {
if (target[i] == 'L') {
arr2.emplace_back('L', i);
} else if(target[i] == 'R') {
arr2.emplace_back('R', i);
}
if (start[i] == 'L') {
arr1.emplace_back('L', i);
} else if (start[i] == 'R') {
arr1.emplace_back('R', i);
}
}
if (arr1.size() != arr2.size()) {
return false;
}
int m = arr1.size();
for (int i = 0; i < m; i++) {
auto [ch1, idx1] = arr1[i];
auto [ch2, idx2] = arr2[i];
if (ch1 != ch2) {
return false;
}
if (ch1 == 'L') {
if (idx1 < idx2) {
return false;
}
} else {
if (idx1 > idx2) {
return false;
}
}
}
return true;
}
};

6115. 统计理想数组的数目

题目

给你两个整数 nmaxValue ,用于描述一个 理想数组 。

对于下标从 0 开始、长度为 n 的整数数组 arr ,如果满足以下条件,则认为该数组是一个 理想数组 :

  • 每个 arr[i] 都是从 1maxValue 范围内的一个值,其中 0 <= i < n
  • 每个 arr[i] 都可以被 arr[i - 1] 整除,其中 0 < i < n
    返回长度为 n 的 不同 理想数组的数目。由于答案可能很大,返回对 109 + 7 取余的结果。

 

示例 1:

输入:n = 2, maxValue = 5
输出:10
解释:存在以下理想数组:
- 以 1 开头的数组(5 个):[1,1]、[1,2]、[1,3]、[1,4]、[1,5]
- 以 2 开头的数组(2 个):[2,2]、[2,4]
- 以 3 开头的数组(1 个):[3,3]
- 以 4 开头的数组(1 个):[4,4]
- 以 5 开头的数组(1 个):[5,5]
共计 5 + 2 + 1 + 1 + 1 = 10 个不同理想数组。

示例 2:

输入:n = 5, maxValue = 3
输出:11
解释:存在以下理想数组:
- 以 1 开头的数组(9 个):
- 不含其他不同值(1 个):[1,1,1,1,1]
- 含一个不同值 2(4 个):[1,1,1,1,2], [1,1,1,2,2], [1,1,2,2,2], [1,2,2,2,2]
- 含一个不同值 3(4 个):[1,1,1,1,3], [1,1,1,3,3], [1,1,3,3,3], [1,3,3,3,3]
- 以 2 开头的数组(1 个):[2,2,2,2,2]
- 以 3 开头的数组(1 个):[3,3,3,3,3]
共计 9 + 1 + 1 = 11 个不同理想数组。

提示:

  • 2 <= n <= 104
  • 1 <= maxValue <= 104

地址

https://leetcode.cn/problems/count-the-number-of-ideal-arrays

题意

动态规划 + 组合数学

思路

  1. 难得见到质量非常高的数学题目,比赛的时候没有做出来。虽然题目挺难,加一些数学的理论,但是还是感觉这类题目有意思,有思考的深度。
  • 解法1:参考解法:排列组合
    由于题目中满足 $arr[i]$ 能够被 $arr[i-1]$ 整除,假设序列为 $[a_0,a_1,a_2, …, a_{n-1}]$,假设相邻的两个元素进行相除可以得到如下序列 $d = [a_0, \frac{a_2}{a_1},\frac{a_3}{a_2}, \cdots \frac{a_i}{a_{i-1}}, \cdots \frac{a_{n-1}}{a_{n-2}}]$,其中令 $d_{i} = \frac{a_i}{a_{i-1}}$,我们可以知道如下递推关系 $a_{i} = \prod \limits_{j=0}^{i} d_j$,因此我们只需要求出数组 $d$ 就可以求出数组 $arr$,如果我们求出数组 $d$ 的数目。我们设 $f[i]$ 表示数组中的最后一个元素 $arr[n-1]$ 的数组的数组,此时我们知道总共的不同的数组的数组为 $tot = \sum \limits_{i=1}^{maxValue} f[i]$。对于数组中最最后一个元素为 $i$,设 $i$ 的质因子为 $p_0,p_1, …, p_{m-1}$ 则我们知道 $i = p_0^{c_0}p_1^{c_1}p_{m-1}^{c_{m-1}}$,跟局之前的推论我们可以知道 $i = p_0^{c_0}p_1^{c_1}p_{m-1}^{c_{m-1}} = \prod \limits_{j=0}^{n-1} d_j$,此时我们只需要将 $C = \sum \limits_{j = 0}^{m-1}c_j$ 这几个因子放入到 $n$ 个位置中,其中每个位置可以重复放多个元素,剩余未放置元素的位置填入 $1$ 即可。这就等价于将 $m$ 种不同颜色的球放入到 $n$ 个不同的盒子中的不同放置方法的数量,这个不太好求,应该有类似的公式可以直接求解,但是还不太清楚这个排列组合的公式怎么求。但是我们知道一个经典问题,将 $k$ 个相同的球放入到 $n$ 个不同的盒子中放置方法为 $C_{n+k-1}^{n-1}$,关于组合数学问题的解释可以参考 「3分钟帮你完全理解排列组合中的隔板问题」,这个解释得非常清晰易懂。因此我们就知道了对于 $f[i] = \prod \limits_{j=0}^{m}C_{n + c_j-1}^{n-1}$,这样我们就很容易的求出答案了。关于乘法逆元的证明:四种方法求乘法逆元!四种!

  • 中间涉及的细节问题:求组合的经典数学公式与乘法逆元的计算公式如下:
    $$
    C_{i}^{j} = C_{i-1}^{j} + C_{i-1}^{j-1} \
    C_{i}^{j} = \frac{A_{i}^{j}}{j!} = \frac{A!}{(i-j)! \times j!} \
    C_{i}^{j} \mod P = \frac{A!}{(i-j)! \times j!} \mod P
    = A! \times ((i-j)!)^{-1} \times (j!)^{-1} \mod P \
    x \times x^{-1} \mod P \equiv 1 \mod P \
    x \times x^{P-2} \mod P \equiv 1 \mod P \
    x^{-1} = x^{P-2} \mod P
    $$
    关于阶乘的逆元的计算:
    $$
    x^{-1} \times y^{-1} = (x \times y)^{-1}\
    (n!)^{-1} \times (n + 1) \times (n + 1)^{-1} \equiv (n!)^{-1} \mod P \
    ((n+1)!)^{-1} \times (n + 1)\equiv (n!)^{-1} \mod P \
    n + 1 \equiv (n+1)! \times (n!)^{-1} \mod P \
    (n + 1)^{-1} \equiv ((n+1)!)^{-1} \times n! \mod P
    $$

  • 复杂度分析:

    • 时间复杂度:$m \log \log m$,$m$ 表示 $maxvalue$。
    • 空间复杂度:$m \times \log m$,$m$ 表示 $maxvalue$。
  • 解法2:参考解法:线性筛选法。感觉这个解法理解起来太难了。用到了非常多的数学理论知识。

代码

  • 解法一:
    const int MAX_FACTOR_NUM = 20;
    const long long MOD = 1e9 + 7;

    class Solution {
    public:
    int idealArrays(int n, int maxValue) {
    int maxn = n + MAX_FACTOR_NUM;
    vector<vector<int>> primer(maxValue + 1);
    vector<vector<long long>> comb(maxn + 1, vector<long long>(MAX_FACTOR_NUM + 1, 1L));
    for (int i = 2; i <= maxValue; i++) {
    int x = i;
    for (int p = 2; p * p <= x; p++) {
    int c = 0;
    while (x % p == 0) {
    c++;
    x /= p;
    }
    if (c > 0) {
    primer[i].emplace_back(c);
    }
    }
    /* x 本身为质数 */
    if (x > 1) {
    primer[i].emplace_back(1);
    }
    }
    for (int i = 2; i <= maxn; i++) {
    for (int j = 1; j < i && j <= MAX_FACTOR_NUM; j++) {
    comb[i][j] = (comb[i-1][j] + comb[i-1][j-1]) % MOD;
    }
    }
    long long ans = 0;
    for (int i = 1; i <= maxValue; i++) {
    long long curr = 1L;
    for (auto c : primer[i]) {
    curr = (curr * comb[n + c - 1][c]) % MOD;
    }
    ans = (ans + curr) % MOD;
    }
    return ans % MOD;
    }
    };

欢迎关注和打赏,感谢支持!

扫描二维码,分享此文章