[Leetcode Weekly Contest]318

链接:LeetCode

[Leetcode]2460. 对数组执行操作

给你一个下标从 0 开始的数组 nums ,数组大小为 n ,且由 非负 整数组成。
你需要对数组执行 n - 1 步操作,其中第 i 步操作(从 0 开始计数)要求对 nums 中第 i 个元素执行下述指令:
如果 nums[i] == nums[i + 1] ,则 nums[i] 的值变成原来的 2 倍,nums[i + 1] 的值变成 0 。否则,跳过这步操作。
在执行完 全部 操作后,将所有 0 移动 到数组的 末尾 。
例如,数组 [1,0,2,0,0,1] 将所有 0 移动到末尾后变为 [1,2,1,0,0,0] 。
返回结果数组。

遍历即可。

class Solution {
    public int[] applyOperations(int[] nums) {
        int i = 0, j = 0, n = nums.length;
        int[] res = new int[n];
        while(i < n) {
            if(nums[i] == 0) {
                i ++;
                continue;
            }
            else if(i < n-1 && nums[i] == nums[i+1]) {
                res[j] = nums[i] * 2;
                i += 2;
                j ++;
            }
            else {
                res[j] = nums[i];
                i ++;
                j ++;
            }
        }
        return res;
    }
}

[Leetcode]2461. 长度为 K 子数组中的最大和

给你一个整数数组 nums 和一个整数 k 。请你从 nums 中满足下述条件的全部子数组中找出最大子数组和:

  • 子数组的长度是 k,且
  • 子数组中的所有元素 各不相同 。

返回满足题面要求的最大子数组和。如果不存在子数组满足这些条件,返回 0 。
子数组 是数组中一段连续非空的元素序列。

哈希表记录数字个数,遍历即可。

class Solution {
    public long maximumSubarraySum(int[] nums, int k) {
        HashMap<Integer, Integer> counter = new HashMap<>();
        long res = 0, result= 0, cur = 0;
        for(int i=0;i<nums.length;++i) {
            int num = nums[i];
            result += num;
            if(i>=k)
            {
                result -= nums[i-k];
                counter.put(nums[i-k], counter.getOrDefault(nums[i-k], 0) - 1);
                if(counter.get(nums[i-k]) == 1) cur ++;
                if(counter.get(nums[i-k]) == 0) cur --;
            }
            counter.put(num, counter.getOrDefault(num, 0) + 1);
            if(counter.get(num) == 1) cur ++;
            if(counter.get(num) == 2) cur --;
            if(cur == k) res = Math.max(res, result);
        }
        return res;
    }
}

[Leetcode]2462. 雇佣 K 位工人的总代价

给你一个下标从 0 开始的整数数组 costs ,其中 costs[i] 是雇佣第 i 位工人的代价。
同时给你两个整数 k 和 candidates 。我们想根据以下规则恰好雇佣 k 位工人:

  • 总共进行 k 轮雇佣,且每一轮恰好雇佣一位工人。
  • 在每一轮雇佣中,从最前面 candidates 和最后面 candidates 人中选出代价最小的一位工人,如果有多位代价相同且最小的工人,选择下标更小的一位工人。
    • 比方说,costs = [3,2,7,7,1,2] 且 candidates = 2 ,第一轮雇佣中,我们选择第 4 位工人,因为他的代价最小 [3,2,7,7,1,2] 。
  • 第二轮雇佣,我们选择第 1 位工人,因为他们的代价与第 4 位工人一样都是最小代价,而且下标更小,[3,2,7,7,2] 。注意每一轮雇佣后,剩余工人的下标可能会发生变化。
  • 如果剩余员工数目不足 candidates 人,那么下一轮雇佣他们中代价最小的一人,如果有多位代价相同且最小的工人,选择下标更小的一位工人。
  • 一位工人只能被选择一次。
    返回雇佣恰好 k 位工人的总代价。

雇佣过程可以用两个最小堆来模拟。
如果两个堆要取的元素重叠了,则可以合并这两个堆,然后取前 k 小的元素。

class Solution {
    public long totalCost(int[] costs, int k, int candidates) {
        int n = costs.length;
        long res = 0;
        if(n <= 2 * candidates) {
            Arrays.sort(costs);
            for(int i=0;i<k;++i) res += costs[i];
            return res;
        }
        PriorityQueue<Integer> head = new PriorityQueue<>();
        PriorityQueue<Integer> tail = new PriorityQueue<>();
        for(int i=0;i<candidates;++i) head.offer(costs[i]);
        for(int j=n-candidates;j<n;++j) tail.offer(costs[j]);
        int i = candidates, j = n - candidates -1;
        for(int ii=0;ii<k;++ii) {
            if(tail.isEmpty() || (!head.isEmpty() && head.peek() <= tail.peek())) {
                res += head.poll();
                if(i<=j) head.offer(costs[i]);
                i++;
            }
            else {
                res += tail.poll();
                if(i<=j) tail.offer(costs[j]);
                j--;
            }
        }
        return res;
    }
}

[Leetcode]2463. 最小移动总距离

X 轴上有一些机器人和工厂。给你一个整数数组 robot ,其中 robot[i] 是第 i 个机器人的位置。再给你一个二维整数数组 factory ,其中 factory[j] = [positionj, limitj] ,表示第 j 个工厂的位置在 positionj ,且第 j 个工厂最多可以修理 limitj 个机器人。
每个机器人所在的位置 互不相同 。每个工厂所在的位置也 互不相同 。注意一个机器人可能一开始跟一个工厂在 相同的位置 。
所有机器人一开始都是坏的,他们会沿着设定的方向一直移动。设定的方向要么是 X 轴的正方向,要么是 X 轴的负方向。当一个机器人经过一个没达到上限的工厂时,这个工厂会维修这个机器人,且机器人停止移动。
任何时刻,你都可以设置 部分 机器人的移动方向。你的目标是最小化所有机器人总的移动距离。
请你返回所有机器人移动的最小总距离。测试数据保证所有机器人都可以被维修。
注意:
所有机器人移动速度相同。
如果两个机器人移动方向相同,它们永远不会碰撞。
如果两个机器人迎面相遇,它们也不会碰撞,它们彼此之间会擦肩而过。
如果一个机器人经过了一个已经达到上限的工厂,机器人会当作工厂不存在,继续移动。
机器人从位置 x 到位置 y 的移动距离为 |y - x| 。

动态规划。
排序后,定义 \(f[i][j]\) 表示前 i 个工厂修复前 j 个机器人的最小移动总距离。
枚举第 i 个工厂修了 k 个机器人,则有

\[f[i][j] = \min\limits_{k=0}^{\min(j, \textit{limit}[i])} f[i-1][j-k] + \sum_{p=j-k+1}^{j} |\textit{robot}[p]-\textit{position}[i]| \]

class Solution {
    public long minimumTotalDistance(List<Integer> robot, int[][] factory) {
        int m = robot.size(), n = factory.length;
        robot.sort((o1, o2) -> o1-o2);
        Arrays.sort(factory, (o1, o2) -> o1[0]-o2[0]);
        long[][] dp = new long[n+1][m+1];
        for(int i=0;i<n+1;++i) {
            for(int j=0;j<m+1;++j) {
                if(i == 0 && j == 0) dp[i][j] = 0;
                else if(i == 0) dp[i][j] = Long.MAX_VALUE;
                else if(j == 0) dp[i][j] = 0;
                else {
                    int canPut = factory[i-1][1];
                    dp[i][j] = dp[i-1][j];
                    long cur = 0;
                    for(int k=1;k<=Math.min(j, canPut);++k) {
                        cur += Math.abs(factory[i-1][0]-robot.get(j-k));
                        if(dp[i-1][j-k] == Long.MAX_VALUE) continue;
                        dp[i][j] = Math.min(dp[i-1][j-k]+cur, dp[i][j]);
                    }
                }
            }
        }
        return dp[n][m];
    }
}

参考:LeetCode

posted @ 2022-11-07 21:46  Jamest  阅读(30)  评论(0编辑  收藏  举报