【周赛总结】周赛360

24-周赛360

过了前三题,第四题没有想出来,那也上分啦

第四题倍增倍增,思路不难,就是很少见

距离原点最远的点【LC2833】

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

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

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

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

  • 思路

    • 计算字符串中'_' 的个数 c o u n t _ count_{\_} count_ s c o r e = c o u n t R − c o u n t L score=count_R-count_L score=countRcountL

    • 贪心:为了获得最远距离'_'的方向应该与score保持一致

      • 如果score大于0,那么最远距离为 s c o r e + c o u n t _ score+count_{\_} score+count_

      • 如果score小于0,那么最远距离为 s c o r e − c o u n t _ score-count_{\_} scorecount_

  • 实现

    class Solution {
        public int furthestDistanceFromOrigin(String moves) {
            int res = 0, cur = 0;
            int count = 0;
            for (char c : moves.toCharArray()){
                if (c == '_'){
                    count++;
                }else if (c == 'L'){
                    cur -= 1;
                }else{
                    cur += 1;
                }
            }
            if (cur > 0){
                res = Math.max(res, cur + count);
            }else{
                res = Math.max(res, Math.abs(cur - count));
            }
            return res;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
      • 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1)

找出美丽数组的最小和【LC2834】

给你两个正整数:ntarget

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

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

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

  • 思路:贪心

    从小到大选择 n n n个数,将已选择的数记录在哈希表中,假设当前数为 x x x,并且哈希表中不存在 t a r g e t − x target-x targetx,那么可以选择数 x x x;否则,跳过 x x x

  • 实现

    class Solution {
        public long minimumPossibleSum(int n, int target) {
            long res = 0L;
            Set<Integer> set = new HashSet<>();
            int num = 1, size = 0;
            while (size < n){
                if (!set.contains(target - num)){
                    res += num;
                    set.add(num);
                    size++;
                }
                num++;
            }
            return res;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
      • 空间复杂度: O ( n ) \mathcal{O}(n) O(n)

使子序列的和等于目标的最少操作次数【LC2835】

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

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

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

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

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

  • 思路:贪心+位运算

    • 由于每个数都是以2的幂存在的,而任何数都可以表示为二进制数,如果nums中所有元素之和小于target,那么我们不能使用nums中的子序列表示target

    • 首先先进行预处理,使用数组count记录 2 i 2^i 2i出现的次数

    • 然后将target进行二进制分解,如果第 i i i位为1,要满足 2 i 2^i 2i,可以有三种方法

      • c o u n t [ i ] count[i] count[i]大于0,那么nums中存在 2 i 2^i 2i
      • nums中存在某些2的幂,其和为 2 i 2^i 2i
      • 如果 j > i j \gt i j>i并且 c o u n t [ j ] count[j] count[j]大于0,那么可以将 2 j 2^j 2j分解 j − i j-i ji次,得到 2 i 2^i 2i

      为了获得 最少操作次数 ,应优先选择方法1和方法2,方法1和方法2都不能满足时,再选择最小的 j j j,进行分解

    • 具体实现时,从低位开始遍历,

      • 首先先判断第 i i i位是否需要满足以及能否满足

        • 能的话次数-1

        • 不能的话,如果之前每位都可以满足才,记录下标

      • 然后判断低位是否有不满足的下标,如果有并且 c o u n t [ i ] > 0 count[i]>0 count[i]>0,那么需要分解 i − n e e d i-need ineed次,再将 c o u n t [ i ] count[i] count[i]减一

      • 最后由于 2 ∗ 2 i = 2 i + 1 2*2^i=2^{i+1} 22i=2i+1,因此将 c o u n t [ i ] / 2 count[i]/2 count[i]/2累加至 c o u n t [ i + 1 ] count[i+1] count[i+1]

  • 实现

    排序计算count

    class Solution {
        public int minOperations(List<Integer> nums, int target) {
            Collections.sort(nums, (o1, o2) -> o1 - o2);
            int res = 0, size = nums.size();
            int[] count = new int[32];
            long sum = 0L;
            for (int i = 0; i < size; i++){
                count[Integer.numberOfTrailingZeros(nums.get(i))]++;
                sum += nums.get(i);
            }
            if (target > sum) return -1;
            int need = 32;
            for (int i = 0; i < 32; i++){
                if (((target >> i) & 1) == 1){
                    if (count[i] == 0){
                        need = Math.min(need, i);
                    }else{
                        count[i]--;
                    }
                }
                
                if (need != 32 && count[i] > 0){
                    count[i]--;
                    res += (i - need);
                    need = 32;
                }
                if (i + 1 < 32){
                    count[i + 1] += count[i] / 2; 
                }
            }
            return res;
     
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n + C ) \mathcal{O}(n+C) O(n+C)
      • 空间复杂度: O ( log ⁡ n + C ) \mathcal{O}(\log n +C) O(logn+C)
  • 实现

    class Solution {
        public int minOperations(List<Integer> nums, int target) {
            long s = 0;
            var cnt = new long[31];
            for (int x : nums) {
                s += x;
                cnt[Integer.numberOfTrailingZeros(x)]++;
            }
            if (s < target)
                return -1;
            int ans = 0, i = 0;
            s = 0;
            while ((1L << i) <= target) {
                s += cnt[i] << i;
                int mask = (int) ((1L << ++i) - 1);
                if (s >= (target & mask))
                    continue;
                ans++; // 一定要找更大的数操作
                for (; cnt[i] == 0; i++)
                    ans++; // 还没找到,继续找更大的数
            }
            return ans;
        }
    }
    
    作者:灵茶山艾府
    链接:https://leetcode.cn/problems/minimum-operations-to-form-subsequence-with-target-sum/solutions/2413344/tan-xin-by-endlesscheng-immn/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

在传球游戏中最大化函数值【LC2836】

给你一个长度为 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 可能含有重复元素。

  • 思路:倍增算法

    • 暴力:一个一个跳跃,必超时

    • 倍增算法优化:预处理每个节点的第 2 j 2^j 2j个祖先节点,以及从每个节点的父节点到其第 2 j 2^j 2j个祖先节点的编号之和

      • p a [ i ] [ j ] pa[i][j] pa[i][j] x x x节点的第 2 j 2^j 2j个祖先节点编号
        p a [ i ] [ j ] = p a [ p a [ i ] [ j − 1 ] ] [ j − 1 ] pa[i][j] = pa[pa[i][j-1]][j-1] pa[i][j]=pa[pa[i][j1]][j1]

      • s u m [ i ] [ j ] sum[i][j] sum[i][j] x x x节点到其第 2 j 2^j 2j个祖先节点的编号之和
        s u m [ i ] [ j ] = s u m [ i ] [ j − 1 ] + s u m [ p a [ i ] [ j − 1 ] ] [ j − 1 ] sum[i][j]=sum[i][j-1]+sum[pa[i][j-1]][j-1] sum[i][j]=sum[i][j1]+sum[pa[i][j1]][j1]

    • 枚举以每个节点为开始节点,对传递次数进行二进制分解,如果第 j j j位为1,那么需要在节点 x x x处,传递 2 j 2^j 2j次,计算分数,取最大值返回

  • 实现

    class Solution {
        public long getMaxFunctionValue(List<Integer> receiver, long k) {
            int n = receiver.size();
            int m = 64 - Long.numberOfLeadingZeros(k);// k的二进制长度
            int[][] pa = new int[m][n];
            long[][] sum = new long[m][n];
            for (int i = 0; i < n; i++){
                pa[0][i] = receiver.get(i);
                sum[0][i] = receiver.get(i);
            }
            for (int i = 1; i < m; i++){
                for (int x = 0; x < n; x++){
                    int p = pa[i - 1][x];// x节点的第2^(i-1)个祖先节点
                    pa[i][x] = pa[i - 1][p];
                    sum[i][x] = sum[i - 1][x] + sum[i - 1][p];
                }
            }
            long res = 0;
            for (int i = 0; i < n; i++){
                long s = i;
                int x = i;
                for (int j = 0; j < m; j++){
                    if (((k >> j) & 1) == 1){
                        s += sum[j][x];
                        x = pa[j][x];
                    }
                }
                res = Math.max(res, s);
            }
            return res;
        }
    }
    
    • 复杂度
      • 时间复杂度: O ( n log ⁡ k ) \mathcal{O}(n \log k) O(nlogk)
      • 空间复杂度: O ( n log ⁡ k ) \mathcal{O}(n \log k) O(nlogk)

你可能感兴趣的:(周赛,算法,leetcode)