ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[Leetcode Weekly Contest]271

2022-03-07 21:32:12  阅读:199  来源: 互联网

标签:nums int res Contest 271 数组 fruits startPos Leetcode


链接:LeetCode

[Leetcode]2103. 环和杆

总计有 n 个环,环的颜色可以是红、绿、蓝中的一种。这些环分布穿在 10 根编号为 0 到 9 的杆上。

给你一个长度为 2n 的字符串 rings ,表示这 n 个环在杆上的分布。rings 中每两个字符形成一个 颜色位置对 ,用于描述每个环:

第 i 对中的 第一个 字符表示第 i 个环的 颜色('R'、'G'、'B')。
第 i 对中的 第二个 字符表示第 i 个环的 位置,也就是位于哪根杆上('0' 到 '9')。
例如,"R3G2B1" 表示:共有 n == 3 个环,红色的环在编号为 3 的杆上,绿色的环在编号为 2 的杆上,蓝色的环在编号为 1 的杆上。

找出所有集齐 全部三种颜色 环的杆,并返回这种杆的数量。

遍历即可。

class Solution {
    public int countPoints(String rings) {
        int res = 0;
        var ringMap = new HashMap<Integer, HashSet<Character>>();
        for(int i=0;i<rings.length();++i)
        {
            if((i&1)!=0)
            {
                int ring = rings.charAt(i) - '0'; // (int) rings.charAt(i)
                var set = ringMap.getOrDefault(ring, new HashSet<Character>());
                set.add(rings.charAt(i-1));
                ringMap.put(ring, set);
            }
        }
        for (HashSet<Character> set:ringMap.values()) {
            if(set.size() == 3) res ++;
        }
        return res;
    }
}

[Leetcode]2104. 子数组范围和

给你一个整数数组 nums 。nums 中,子数组的 范围 是子数组中最大元素和最小元素的差值。
返回 nums 中 所有 子数组范围的 和 。
子数组是数组中一个连续 非空 的元素序列。

暴力算法,即记录子数组最大元素和最小元素,循环即可,时间复杂度\(O(N^2)\)。

class Solution {
    public long subArrayRanges(int[] nums) {
        int n = nums.length;
        long res = 0;
        for(int i=0;i<n;++i){
            int mn = nums[i], mx = nums[i];
            for (int j=i+1;j<n;++j) {
                mn = Math.min(mn, nums[j]);
                mx = Math.max(mx, nums[j]);
                res += mx - mn;
            }
        }
        return res;
    }
}

另外, 我们可以考虑每个元素作为最大值出现在了多少子数组中,以及作为最小值出现在了多少子数组中,即分别计算每个元素对于最大值和最小值的贡献。令left[i] 为左侧严格大于 num[i] 的最近元素位置(不存在时为 -1),right[i] 为右侧大于等于 num[i] 的最近元素位置(不存在时为 n),那么为了包含索引i,该子数组的区间起始点有(i−left[i])种情况,终止点有(right[i]−i)种情况,两者的乘积就是子区间的个数。
这里求left以及right数组就需要用到单调栈了。

class Solution {
    public long subArrayRanges(int[] nums) {
        int n = nums.length;
        long res = 0;
        int[] leftLarger = new int[n], leftSmaller = new int[n];
        int[] rightLarger = new int[n], rightSmaller = new int[n];
        Arrays.fill(leftLarger, -1);
        Arrays.fill(leftSmaller, -1);
        Arrays.fill(rightLarger, n);
        Arrays.fill(rightSmaller, n);
        arrayLeft(nums,leftLarger,leftSmaller);
        arrayRight(nums,rightLarger,rightSmaller);
        for(int i=0;i<n;i++)
        {
            res += 1L * nums[i] *  ((rightLarger[i]-i)*(i-leftLarger[i]) - (rightSmaller[i]-i)*(i-leftSmaller[i]));
        }
        return res;
    }

    //   get first right index > nums[i] with first right index < nums[i]
    public void arrayRight(int[] nums, int[] rightLarger, int[] rightSmaller) {
        int n = nums.length;
        Deque<Integer> increasedStack = new ArrayDeque<>();
        Deque<Integer> decreasedStack = new ArrayDeque<>();
        for(int i=0;i<n;++i) {
            while(!increasedStack.isEmpty() && nums[increasedStack.peek()] > nums[i]){
                rightSmaller[increasedStack.pop()] = i;
            }
            while(!decreasedStack.isEmpty() && nums[decreasedStack.peek()] < nums[i]){
                rightLarger[decreasedStack.pop()] = i;
            }
            increasedStack.push(i);
            decreasedStack.push(i);
        }
    }
    //   get first left index >= nums[i] with first left index <= nums[i]
    public void arrayLeft(int[] nums, int[] leftLarger, int[] leftSmaller) {
        int n = nums.length;
        Deque<Integer> increasedStack = new ArrayDeque<>();
        Deque<Integer> decreasedStack = new ArrayDeque<>();
        for(int i=n-1;i>=0;--i) {
            while(!increasedStack.isEmpty() && nums[increasedStack.peek()] >= nums[i]){
                leftSmaller[increasedStack.pop()] = i;
            }
            while(!decreasedStack.isEmpty() && nums[decreasedStack.peek()] <= nums[i]){
                leftLarger[decreasedStack.pop()] = i;
            }
            increasedStack.push(i);
            decreasedStack.push(i);
        }
    }
}

[Leetcode]2105. 给植物浇水 II

Alice 和 Bob 打算给花园里的 n 株植物浇水。植物排成一行,从左到右进行标记,编号从 0 到 n - 1 。其中,第 i 株植物的位置是 x = i 。

每一株植物都需要浇特定量的水。Alice 和 Bob 每人有一个水罐,最初是满的 。他们按下面描述的方式完成浇水:

Alice 按 从左到右 的顺序给植物浇水,从植物 0 开始。Bob 按 从右到左 的顺序给植物浇水,从植物 n - 1 开始。他们 同时 给植物浇水。
如果没有足够的水 完全 浇灌下一株植物,他 / 她会立即重新灌满浇水罐。
不管植物需要多少水,浇水所耗费的时间都是一样的。
不能 提前重新灌满水罐。
每株植物都可以由 Alice 或者 Bob 来浇水。
如果 Alice 和 Bob 到达同一株植物,那么当前水罐中水更多的人会给这株植物浇水。如果他俩水量相同,那么 Alice 会给这株植物浇水。
给你一个下标从 0 开始的整数数组 plants ,数组由 n 个整数组成。其中,plants[i] 为第 i 株植物需要的水量。另有两个整数 capacityA 和 capacityB 分别表示 Alice 和 Bob 水罐的容量。返回两人浇灌所有植物过程中重新灌满水罐的 次数 。

双指针。根据题意遍历即可。

class Solution {
    public int minimumRefill(int[] plants, int capacityA, int capacityB) {
        int left = 0, right = plants.length - 1;
        int res = 0, left_capacity = capacityA, right_capacity = capacityB;
        while(left <= right) {
            if (left == right) {
                if(Math.max(left_capacity,right_capacity) < plants[left]) res ++;
            }
            else {
                if(left_capacity < plants[left]) {
                    left_capacity = capacityA;
                    res ++;
                }
                if(right_capacity < plants[right]) {
                    right_capacity = capacityB;
                    res ++;
                }
                left_capacity -= plants[left];
                right_capacity -= plants[right];
            }
            left ++;
            right --;
        }
        return res;
    }
}

[Leetcode]2106. 摘水果

在一个无限的 x 坐标轴上,有许多水果分布在其中某些位置。给你一个二维整数数组 fruits ,其中 fruits[i] = [positioni, amounti] 表示共有 amounti 个水果放置在 positioni 上。fruits 已经按 positioni 升序排列 ,每个 positioni 互不相同 。

另给你两个整数 startPos 和 k 。最初,你位于 startPos 。从任何位置,你可以选择 向左或者向右 走。在 x 轴上每移动 一个单位 ,就记作 一步 。你总共可以走 最多 k 步。你每达到一个位置,都会摘掉全部的水果,水果也将从该位置消失(不会再生)。

返回你可以摘到水果的 最大总数 。

显然在最优的方案中,最多掉头一次。那么就有两种情况:

  • 先往左,如果还有步数,再往右
  • 先往右,如果还有步数,再往左

针对这两种情况,我们无非是需要计算线段 [l, r] 覆盖了多少水果。
线段有多少种可能呢?假设人向左走 \(y\) 步,然后回到原点,再向右走 \(x\) 步,那么区间长度就是:\(x + 2y\) ,其中 \(x + 2y \le k\) ,区间表示为 [startPos - y, startPos + x] 。
同理,如果向右走 \(y\) 步,然后回到原点,再向左走 \(x\) 步,那么区间表示为 [startPos - x, startPos + y] 。
所以我们枚举 \(y\) 长度就可以把所有最长线段都枚举出来。下面我们计算线段 [l, r]水果个数的时候,就可以用前缀和的知识,由于并不是在每一个点都有水果,我们就优化到线段[l, r]所覆盖的有水果区间[l_hi, r_lo]上, 其中l_hi是指l右侧第一个有水果的索引,同理,r_lo指r左侧第一个有水果的索引。这里就需要二分法来进行优化。
最终解决方案即遍历+前缀和+二分

class Solution {
    public int maxTotalFruits(int[][] fruits, int startPos, int k) {
        int res = 0, n = fruits.length, cur = 0;
        int[] preSum = new int[n];
        for(int i=0;i<n; ++i) {
            cur += fruits[i][1];
            preSum[i] = cur;
        }
        for(int x=Math.min(startPos,fruits[0][0]); x<=startPos; ++x) {
            if(startPos-x > k) continue;
            int xi = GetSmaller(fruits, x);
            int y = Math.max(k - 2*(startPos-x) + startPos, startPos + (int) ((k-(startPos-x)) / 2));
            int yi = GetSmallerOrEqualIndex(fruits, y);
            if(yi == -1) continue;
            else if(xi == -1)  res = Math.max(res, preSum[yi]);
            else res = Math.max(res, preSum[yi]- preSum[xi]);
        }
        return res;
    }

    public int GetSmallerOrEqualIndex(int[][] fruits, int target) {
        int lo = 0, hi = fruits.length-1;
        while(lo <= hi) {
            int mid = lo + ((hi - lo) >> 1);
            int pos = fruits[mid][0];
            if (pos == target) return mid;
            if (pos > target) hi = mid - 1;
            else lo = mid + 1;
        }
        return hi;
    }

    public int GetSmaller(int[][] fruits, int target) {
        int lo = 0, hi = fruits.length-1;
        while(lo <= hi) {
            int mid = lo + ((hi - lo) >> 1);
            int pos = fruits[mid][0];
            if (pos >= target) hi = mid - 1;
            else lo = mid + 1;
        }
        return hi;
    }
}

Leetcode

标签:nums,int,res,Contest,271,数组,fruits,startPos,Leetcode
来源: https://www.cnblogs.com/hellojamest/p/15978130.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有