[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 个机器人,则有
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