leetcode 第360场周赛

这篇具有很好参考价值的文章主要介绍了leetcode 第360场周赛。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

总结

好久没参加leetcode周赛了,比赛时间都从两小时变成了一个半小时。这次周赛由两道签到题和两道中等难度题组成,严格来说最后一道的难度也可以视为hard,但是只要想到正确的思路,编码还是比较容易的。

比赛链接:leetcode 第 360 场周赛

题目列表

1.距离原点最远的点

题目描述

给你一个长度为 n 的字符串 moves ,该字符串仅由字符 'L''R''_' 组成。字符串表示你在一条原点为 0 的数轴上的若干次移动。

你的初始位置就在原点(0),第 i 次移动过程中,你可以根据对应字符选择移动方向:

  • 如果 moves[i] = 'L'moves[i] = '_' ,可以选择向左移动一个单位距离
  • 如果 moves[i] = 'R'moves[i] = '_' ,可以选择向右移动一个单位距离

移动 n 次之后,请你找出可以到达的距离原点 最远 的点,并返回 从原点到这一点的距离

示例 1:

输入:moves = "L_RL__R"
输出:3
解释:可以到达的距离原点 0 最远的点是 -3 ,移动的序列为 "LLRLLLR" 。

示例 2:

输入:moves = "_R__LL_"
输出:5
解释:可以到达的距离原点 0 最远的点是 -5 ,移动的序列为 "LRLLLLL" 。

示例 3:

输入:moves = "_______"
输出:7
解释:可以到达的距离原点 0 最远的点是 7 ,移动的序列为 "RRRRRRR" 。

提示:

  • 1 <= moves.length == n <= 50
  • moves 仅由字符 'L''R''_' 组成

分析

本题主要是理解题意,moves是’_'的时候可以选择左移也可以选择右移,要求最终到达的位置离原点最远。

移动的顺序无所谓,因此经过若干次’L’和’R’的移动后,位置P是唯一的,此时只剩下若干个’_‘的操作符,如果P是在原点左边,’_'就继续左移,否则右移。具体实现时候只需要统计下各个操作字符的个数,然后返回 a b s ( L − R ) + c n t ( _ ) abs(L-R) + cnt(\_) abs(LR)+cnt(_)即可。

代码

class Solution {
public:
    int furthestDistanceFromOrigin(string moves) {
        int l = 0, r = 0, t = 0;
        for(int i = 0;i < moves.size();i++) {
            if (moves[i] == 'L') l++;
            else if(moves[i] == 'R') r++;
            else t++;
        }
        return max(l, r)  - min(l, r) + t;
    }
};

2.找出美丽数组的最小和

题目描述

给你两个正整数:ntarget

如果数组 nums 满足下述条件,则称其为 美丽数组

  • nums.length == n.
  • nums 由两两互不相同的正整数组成。
  • 在范围 [0, n-1] 内,不存在 两个 不同 下标 ij ,使得 nums[i] + nums[j] == target

返回符合条件的美丽数组所可能具备的 最小 和。

示例 1:

输入:n = 2, target = 3
输出:4
解释:nums = [1,3] 是美丽数组。
- nums 的长度为 n = 2 。
- nums 由两两互不相同的正整数组成。
- 不存在两个不同下标 i 和 j ,使得 nums[i] + nums[j] == 3 。
可以证明 4 是符合条件的美丽数组所可能具备的最小和。

示例 2:

输入:n = 3, target = 3
输出:8
解释:
nums = [1,3,4] 是美丽数组。 
- nums 的长度为 n = 3 。 
- nums 由两两互不相同的正整数组成。 
- 不存在两个不同下标 i 和 j ,使得 nums[i] + nums[j] == 3 。
可以证明 8 是符合条件的美丽数组所可能具备的最小和。

示例 3:

输入:n = 1, target = 1
输出:1
解释:nums = [1] 是美丽数组。

提示:

  • 1 <= n <= 10^5
  • 1 <= target <= 10^5

分析

简单的模拟题,从1开始枚举数组里的正整数,添加i前确认下数组里不存在target - i的数字,添加后就使用哈希表记录下已添加的数字,直至枚举到n个数,求和即可。

代码

class Solution {
public:
    unordered_map<int,int> m;
    long long minimumPossibleSum(int n, int target) {
        long long res = 0;
        int cnt = 0;
        int num = 1;
        while(cnt < n) {
            if (!m.count(target - num)) {
                cnt++;
                m[num] = 1;
                res += num;
            }
            num++;
        }
        return res;
    }
};

3.使子序列的和等于目标的最少操作次数

题目描述

给你一个下标从 0 开始的数组 nums ,它包含 非负 整数,且全部为 2 的幂,同时给你一个整数 target

一次操作中,你必须对数组做以下修改:

  • 选择数组中一个元素 nums[i] ,满足 nums[i] > 1
  • nums[i] 从数组中删除。
  • nums末尾 添加 两个 数,值都为 nums[i] / 2

你的目标是让 nums 的一个 子序列 的元素和等于 target ,请你返回达成这一目标的 最少操作次数 。如果无法得到这样的子序列,请你返回 -1

数组中一个 子序列 是通过删除原数组中一些元素,并且不改变剩余元素顺序得到的剩余数组。

示例 1:

输入:nums = [1,2,8], target = 7
输出:1
解释:第一次操作中,我们选择元素 nums[2] 。数组变为 nums = [1,2,4,4] 。
这时候,nums 包含子序列 [1,2,4] ,和为 7 。
无法通过更少的操作得到和为 7 的子序列。

示例 2:

输入:nums = [1,32,1,2], target = 12
输出:2
解释:第一次操作中,我们选择元素 nums[1] 。数组变为 nums = [1,1,2,16,16] 。
第二次操作中,我们选择元素 nums[3] 。数组变为 nums = [1,1,2,16,8,8] 。
这时候,nums 包含子序列 [1,1,2,8] ,和为 12 。
无法通过更少的操作得到和为 12 的子序列。

示例 3:

输入:nums = [1,32,1], target = 35
输出:-1
解释:无法得到和为 35 的子序列。

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 2^30
  • nums 只包含非负整数,且均为 2 的幂。
  • 1 <= target < 2^31

分析

首先,数组里面的元素都是2的整数次幂,只要数组的和不小于target,那么经过若干次拆分一定可以使得子序列的和等于target。

不难想到将target使用二进制表示,拆分成若干个2的整数次幂数字的和,然后在数组里面找到这些数字,如果不存在,要么拆分,要么使用更小的数累加。

nums = [1,32,1,2], target = 12

第二个示例比较适合用来分析,target为12=4 + 8,数组里找不到4,所以使用两个1和一个2拼到一起了;然后32拆分两次得到8。什么时候使用拼接,什么时候使用拆分?想清楚这个问题本题就可以求解了。

求解思路如下:

  • 将target的二进制表示存入vector s,将数组里面的元素二进制表示也存入一个vector v。
  • 自低位到高位遍历target的二进制vector,如果该位置是1,就在v中查找有没有对应的元素,有就将该元素数量减一,将多余的元素除以2累加到更高位的元素数量,没有就继续往高位查找,找到高位元素后一步步分解,分解到需要的元素重复前面的步骤;如果s的该位置是0,也需要将v中对应元素除以2累加到高位。

以上面例子为例,12=(1100)2,最低位是0,而nums里面有2个1,将这两个1累加起来增加2的个数,得到[32,2,2];第二位还是0,将两个2累加起来得到[32,4],第三位是1,并且数组里面出现了累加的4;第四位是1,数组里面没有找到8,向高位找到了32,拆分两次得到了8,最终拆分的次数是2。

因此,本题的关键就是想到对于没用完的元素,累加到高位继续用,想到了这点直接模拟就可以解决了。

代码

class Solution {
public:
    int index(int n) {
        if (n == 1) return 0;
        return index(n / 2) + 1;
    }
    int minOperations(vector<int>& nums, int target) {
        vector<int> v(32, 0);
        vector<int> s;
        int t = target;
        while(t) {
            s.push_back(t & 1);
            t >>= 1;
        }
        int n = s.size();
        long long sum = 0;
        for(auto x : nums) {
            v[index(x)]++;
            sum += x;
        }
        if (sum < target) return -1;
        int res = 0;
        for (int i = 0; i < n;i++) {
            if (s[i]) {
                if (v[i]) {
                    v[i]--;
                    v[i+1] += v[i] / 2;
                    continue;
                }
                int j = i + 1;
                while(!v[j]) j++;
                while(j != i) {
                    v[j]--;
                    v[--j] += 2;
                    res++;
                }
            } else {
                v[i+1] += v[i] / 2;
            }
        }
        return res;
    }
};

4.在传球游戏中最大化函数值

题目描述

给你一个长度为 n 下标从 0 开始的整数数组 receiver 和一个整数 k

总共有 n 名玩家,玩家 编号 互不相同,且为 [0, n - 1] 中的整数。这些玩家玩一个传球游戏,receiver[i] 表示编号为 i 的玩家会传球给编号为 receiver[i] 的玩家。玩家可以传球给自己,也就是说 receiver[i] 可能等于 i

你需要从 n 名玩家中选择一名玩家作为游戏开始时唯一手中有球的玩家,球会被传 恰好 k 次。

如果选择编号为 x 的玩家作为开始玩家,定义函数 f(x) 表示从编号为 x 的玩家开始,k 次传球内所有接触过球玩家的编号之 ,如果有玩家多次触球,则 累加多次 。换句话说, f(x) = x + receiver[x] + receiver[receiver[x]] + ... + receiver(k)[x]

你的任务时选择开始玩家 x ,目的是 最大化 f(x)

请你返回函数的 最大值

注意:receiver 可能含有重复元素。

示例 1:

传递次数 传球者编号 接球者编号 x + 所有接球者编号
2
1 2 1 3
2 1 0 3
3 0 2 5
4 2 1 6
输入:receiver = [2,0,1], k = 4
输出:6
解释:上表展示了从编号为 x = 2 开始的游戏过程。
从表中可知,f(2) 等于 6 。
6 是能得到最大的函数值。
所以输出为 6 。

示例 2:

传递次数 传球者编号 接球者编号 x + 所有接球者编号
4
1 4 3 7
2 3 2 9
3 2 1 10
输入:receiver = [1,1,1,2,3], k = 3
输出:10
解释:上表展示了从编号为 x = 4 开始的游戏过程。
从表中可知,f(4) 等于 10 。
10 是能得到最大的函数值。
所以输出为 10 。

提示:

  • 1 <= receiver.length == n <= 10^5
  • 0 <= receiver[i] <= n - 1
  • 1 <= k <= 10^10

分析

本题常规解法,找环的起点比较繁琐,而且即使找到了环,如果环的长度是10w级别的,遍历下n个起点,时间也会超限。比较好的解法是使用DP+倍增。一共要传k轮,可以对k进行二进制拆分,比如k = 13,可以拆除1 + 4 + 8,也就是先传1轮,再传4轮,最后传8轮。

状态表示: f [ i ] [ j ] f[i][j] f[i][j]表示从 i i i开始,传 2 j 2^j 2j轮经过的编号之和,注意这里的编号不包括 i i i本身,因为一旦包含了 i i i,第一次传到了 t t t,已经统计了 t t t的编号,第二次从 t t t开始传,就重复统计了,统计编号时不包含起点,最后统计结果时再加上起点的编号即可。

另外,除了需要记录传 2 j 2^j 2j后的编号之和,还需要记录位置,这样才能确定下一轮传输的起点,即 p o s [ i ] [ j ] pos[i][j] pos[i][j]表示从 i i i开始,传 2 j 2^j 2j轮到达的编号。

状态转移方程:
f [ i ] [ j ] = f [ i ] [ j − 1 ] + f [ p o s [ i ] [ j − 1 ] ] [ j − 1 ] f[i][j] = f[i][j-1] + f[pos[i][j-1]][j-1] f[i][j]=f[i][j1]+f[pos[i][j1]][j1]

p o s [ i ] [ j ] = p o s [ p o s [ i ] [ j − 1 ] ] [ j − 1 ] pos[i][j] = pos[pos[i][j-1]][j-1] pos[i][j]=pos[pos[i][j1]][j1]

也就是从 i i i出发走 2 j 2^j 2j步,只需要从 i i i出发先走 2 j − 1 2^{j-1} 2j1步到达 p o s [ i ] [ j − 1 ] pos[i][j-1] pos[i][j1]的位置,再从该位置出发再走 2 j − 1 2^{j-1} 2j1步。

状态边界: f [ i ] [ 0 ] = r e c e i v e r [ i ] f[i][0]=receiver[i] f[i][0]=receiver[i] p o s [ i ] [ 0 ] = r e c e i v e r [ i ] pos[i][0]=receiver[i] pos[i][0]=receiver[i]

另外,还需要记录下走 k k k轮,每次走到的中转的位置,这样才能在此基础上找到下次的起点。

由于状态转移每次只使用了上一层的状态,所以可以使用滚动数组实现,节省内存空间。

具体实现见代码:文章来源地址https://www.toymoban.com/news/detail-680222.html

代码

class Solution {
public:
    static const int N = 100005;
    long long f[N][2];
    int pos[N][2], pre[N];
    long long ans[N];
    long long getMaxFunctionValue(vector<int>& r, long long k) {
        int n = r.size();
        for (int i = 0; i < n;i++) {
            f[i][0] = r[i];
            pos[i][0] = r[i];
            pre[i] = i;
        }
        if (k & 1) {
            for(int i = 0; i < n;i++) {
                ans[i] = f[i][0];
                pre[i] = pos[i][0];
            }
        }
        for (int i = 1; i < 35;i++) {
            int t1 = i & 1, t2 = 1 - t1;
            for (int j = 0; j < n;j++) {
                f[j][t1] = f[j][t2] + f[pos[j][t2]][t2];
                pos[j][t1] = pos[pos[j][t2]][t2];
            }
            if(k >> i & 1) {
                for(int j = 0; j < n;j++) {
                    ans[j] += f[pre[j]][t1];
                    pre[j] = pos[pre[j]][t1];
                }
            }
        }
        long long res = 0;
        for(int i = 0; i < n;i++) res = max(res, i + ans[i]);
        return res;
    }
};

到了这里,关于leetcode 第360场周赛的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [LeetCode周赛复盘] 第 359 场周赛20230820

    T1 模拟。 T2 数学贪心。 T3 dp。 T4 分组+滑窗。 2828. 判别首字母缩略词 1. 题目描述 2. 思路分析 按题意模拟即可。 3. 代码实现 2829. k-avoiding 数组的最小总和 1. 题目描述 2. 思路分析 贪心 1~k-1中,选了1就不能选k-1;选了2就不能选k-2… 因此可以选1~k//2 剩余的从k开始向上选。 可以

    2024年02月11日
    浏览(28)
  • [LeetCode周赛复盘] 第 353 场周赛20230709

    感觉有奖品大家都来了。 T1 数学。 T2 dp。 T3 dp。 T4 差分/BIT RUPQ。 6451. 找出最大的可达成数字 1. 题目描述 2. 思路分析 为了使x num在t步内相同,需要相向而行,每步最大缩短距离是2,那么t步距离是2t。 3. 代码实现 6899. 达到末尾下标所需的最大跳跃次数 1. 题目描述 2. 思路分

    2024年02月15日
    浏览(21)
  • [LeetCode周赛复盘] 第 348场周赛20230604

    这场可惜了。 T1 模拟。 T2 模拟。 T3 倒序计算。 T4 同时限制上下界的数位DP。 6462. 最小化字符串长度 1. 题目描述 2. 思路分析 题意仔细想一下就会发现,其实会将每个字符仅留1个。 3. 代码实现 6424. 半有序排列 1. 题目描述 2. 思路分析 由于只能相邻交换来移动,因此每次只能

    2024年02月08日
    浏览(30)
  • LeetCode第347场周赛

    2023.5.28LeetCode第347场周赛 从最后一位开始遍历,为0则跳过 暴力模拟 对于每个 s[i] != s[i - 1] ,要使其相等 有两种选择,翻转前 i 个,或者翻转后 n - i 个,选择代价最小的方案 动态规划 从小到大枚举所有值,每个值一定是从更小的数转移而来 定义动态规划数组f, f[i][j] 表示

    2024年02月06日
    浏览(65)
  • LeetCode第343场周赛

    2023.4.30LeetCode第343场周赛 根据题意模拟 使用哈希表记录每个数出现的位置,再用m+n个集合记录每一行和每一列被涂满的格子数,若某行或某列全部被涂满则返回答案 BFS 首先将距离大于两点的曼哈顿距离的特殊路径去掉 每个点考虑经过每个特殊路径到达,分成两段,一段是当

    2024年02月02日
    浏览(29)
  • LeetCode第354场周赛

    给你一个下标从 1 开始、长度为 n 的整数数组 nums 。 对 nums 中的元素 nums[i] 而言,如果 n 能够被 i 整除,即 n % i == 0 ,则认为 num[i] 是一个 特殊元素 。 返回 nums 中所有 特殊元素 的 平方和 。 直接模拟就好了 给你一个下标从 0 开始的整数数组 nums 和一个 非负 整数 k 。 在一

    2024年02月16日
    浏览(31)
  • leetcode第 357/358 场周赛

    可能别人有更好的解法,我这写法是不断往线段树中插入数值,每次先插入nums[i-x],然后搜索(1到i)中的最大值和(i到max)中的最小值去更新ans。 看了看别人题解,直接用set写是真的牛。自己还是见识短浅了。 暴力乱搞,考虑两种极端情况,一种无脑选profit大的,一种优先选

    2024年02月12日
    浏览(30)
  • leetcode第354场周赛补题

    6889. 特殊元素平方和 - 力扣(LeetCode) 思路:模拟 6929. 数组的最大美丽值 - 力扣(LeetCode) 思路:排序+双指针 6927. 合法分割的最小下标 - 力扣(LeetCode) 思路:哈希+枚举 6924. 最长合法子字符串的长度 - 力扣(LeetCode) 思路:哈希+双指针

    2024年02月16日
    浏览(26)
  • Leetcode 第 365 场周赛题解

    思路 暴力。 代码 复杂度分析 时间复杂度:O(n 3 ),其中 n 是数组 nums 的长度。 空间复杂度:O(1)。 思路 枚举 k,我们需要知道 k 左边 nums[i]−nums[j] 的最大值。 使用 pre_max 维护 k 之前的 nums[i] 的最大值,使用 max_diff 维护 nums[i]−nums[j] 的最大值。 每次遍历一个 nums[i],都更新

    2024年02月07日
    浏览(23)
  • LeetCode 第 388 场周赛个人题解

    目录 100233. 重新分装苹果 原题链接 思路分析 AC代码 100247. 幸福值最大化的选择方案 原题链接 思路分析 AC代码 100251. 数组中的最短非公共子字符串 原题链接 思路分析 AC代码 100216. K 个不相交子数组的最大能量值 原题链接 思路分析 AC代码 100233. 重新分装苹果 直接模拟 降序排

    2024年03月15日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包