LeetCode双周赛110

5 篇文章 0 订阅
5 篇文章 0 订阅
文章介绍了四道LeetCode编程题目,涉及数组操作、最大公约数计算、区间划分和元素值优化。A题涉及账户余额计算,B题是合并链表并插入最大公约数节点,C题讨论数组元素同步替换最少操作,D题则解决数组动态更新后的元素和限制问题。
摘要由CSDN通过智能技术生成

LeetCode双周赛110

A题

代码:

public int accountBalanceAfterPurchase(int purchaseAmount) {
    int t = 100 - (purchaseAmount / 10) * 10;
    purchaseAmount %= 10;
    if (purchaseAmount >= 5) {
        t -= 10;
    }
    return t;
}
B题

两两链表插入一个最大公约数节点

代码:

public ListNode insertGreatestCommonDivisors(ListNode head) {
    if (head.next != null) {
        var cur = head.next;
        merge(head, cur);
        insertGreatestCommonDivisors(cur);
    }
    return head;
}

public int f(int x, int y) {
    if (y == 0) {
        return x;
    }
    return f(y, x % y);
}
private void merge(ListNode pre, ListNode next) {
    int value = f(pre.val, next.val);
    ListNode node = new ListNode(value);
    pre.next = node;
    node.next = next;
}
C题

给你一个下标从 0 开始长度为 n 的数组 nums

每一秒,你可以对数组执行以下操作:

  • 对于范围在 [0, n - 1] 内的每一个下标 i ,将 nums[i] 替换成 nums[i]nums[(i - 1 + n) % n] 或者 nums[(i + 1) % n] 三者之一。

注意,所有元素会被同时替换。

请你返回将数组 nums 中所有元素变成相等元素所需要的 最少 秒数。

思路:本质上就是找出每个同源元素分割的所有区间,求出区间的最大值。

代码:

public int minimumSeconds(List<Integer> nums) {
    Map<Integer, List<Integer>> map = new HashMap<>();
    int n = nums.size() - 1;
    for (int i = 0; i < nums.size(); i++) {
        map.computeIfAbsent(nums.get(i), a -> new ArrayList<>()).add(i);
    }
    int mn = Integer.MAX_VALUE;
    for (var list : map.values()) {
        int len = list.get(0) + (n - list.get(list.size() - 1));
        len = (len + 1) >> 1;
        int ans = len;
        for (int i = 1; i < list.size(); i++) {
            len = list.get(i) - list.get(i - 1) - 1;
            len = (len + 1) >> 1;
            ans = Math.max(len, ans);
        }
        mn = Math.min(ans, mn);
    }
    return mn;
}
D题

给你两个长度相等下标从 0 开始的整数数组 nums1nums2 。每一秒,对于所有下标 0 <= i < nums1.lengthnums1[i] 的值都增加 nums2[i] 。操作 完成后 ,你可以进行如下操作:

  • 选择任一满足 0 <= i < nums1.length 的下标 i ,并使 nums1[i] = 0

同时给你一个整数 x

请你返回使 nums1 中所有元素之和 小于等于 x 所需要的 最少 时间,如果无法实现,那么返回 -1

思路:首先我们要明白元素之和最小的最优解是数组nums1最多每个元素操作一次的情况下达成,也就是最多操作n次,又因为每次操作的收益都是正向收益,所以每一秒都应当进行依次操作,所以假设最优解为t,则t<=n;

明白这个结论之后,我们可以枚举0 ~ n秒,获得每次都执行操作所能得到的一个最大收益。

我们现在来探讨收益,对于每一秒j来说,我们将一个元素i置为0,则收益是nums1[i] + nums2[i] * j,nums1[i]是一个固定值,nums2[i] * j随着j的增长逐渐增大,所以我们对于nums2进行一个从小到大的排序,因为nums2单调递增,所以选择删除的时间也应该是单调递增的,题目要求也就变成了从数组中选长度为j的子数组,求子数组的最大收益,也就是一个简单的dp题。

代码如下:

class Solution {
    public int minimumTime(List<Integer> nums1, List<Integer> nums2, int x) {
        // 第x次操作 价值为 nums1[i] + j * nums2[i]
        int len = nums1.size();
        long sum1 = 0L, sum2 = 0L;
        Integer[] ids = new Integer[len];
        for (int i = 0; i < len; i++) {
            sum1 += nums1.get(i);
            sum2 += nums2.get(i);
            ids[i] = i;
        }
        Arrays.sort(ids, Comparator.comparingInt(nums2::get));
        var dp = new long[len][len + 1];
        for (int i = 0; i < len; i++) {
            if (i == 0) {
                dp[0][1] = nums1.get(ids[0]) + nums2.get(ids[0]);
                continue;
            }
            for (int j = 1; j <= i + 1; j++) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + nums1.get(ids[i]) + (long) nums2.get(ids[i]) * j);
            }
        }
        for (int i = 0; i <= len; i++) {
            long sum = sum1 + i * sum2;
            sum -= dp[len - 1][i];
            if (sum <= x) {
                return i;
            }
        }
        return -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值