• [Leetcode Weekly Contest]271


    链接: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

  • 相关阅读:
    深入学习SlidingMenu 2015-06-12 20:27 856人阅读 评论(0) 收藏
    Android 判断SD卡是否存在及容量查询
    第三方登录,授权,分享
    GLSurfaceView用法详解
    Java/android面试题
    SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问
    填充数字以达到位数
    web api post
    .net测试方法效率获取系统当前时间
    vs2012更新问题
  • 原文地址:https://www.cnblogs.com/hellojamest/p/15978130.html
Copyright © 2020-2023  润新知