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周赛复盘] 第 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日
    浏览(29)
  • [LeetCode周赛复盘] 第 348场周赛20230604

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

    2024年02月08日
    浏览(37)
  • [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日
    浏览(33)
  • LeetCode第354场周赛

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

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

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

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

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

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

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

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

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

    2024年02月16日
    浏览(35)
  • 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日
    浏览(32)
  • LeetCode 第385场周赛个人题解

    目录 100212. 统计前后缀下标对 I 原题链接 题目描述 接口描述 思路分析 代码详解 100229. 最长公共前缀的长度 原题链接 题目描述 接口描述 思路分析 代码详解 100217. 出现频率最高的素数 原题链接 题目描述 接口描述 思路分析 代码详解 100212. 统计前后缀下标对 II 原题链接 题目

    2024年02月19日
    浏览(58)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包