上岸算法 I LeetCode Weekly Contest 217解题报告

No.1 最富有客户的资产总量

解题思路

求子数组和的最大值. 可以使用 Java 8 之后的 stream 一句话搞定.

代码展示

classSolution{

    publicintmaximumWealth(int[][] accounts){

        return Arrays.stream(accounts).

                map(i -> Arrays.stream(i).sum()).

                max(Integer::compareTo).get();

    }

}

No.2 找出最具竞争力都子序列

解题思路

即找到长度为 k 的字典序最小的子序列. 贪心地每次取最小的数即可.

但是我们不能取全局范围的最小数, 比如说第一次取, 我们只能在 [0, n - k] 的下标范围内取最小的, 假设取到的最小值的下标为 x, 那么第二次取就应该在 [x, n - k + 1] 的下标范围内取最小的.

可以使用优先队列完成.

代码展示

class Solution {

    class Number {

        int num;

        int idx;

        public Number(int num, int idx) {

            this.num = num;

            this.idx = idx;

        }

    }

    public int[] mostCompetitive(int[] nums, int k) {

        // 优先按照数值大小排序, 大小相同则按照下标排序

        PriorityQueue<Number> heap = new PriorityQueue<>((a, b) -> a.num == b.num ? a.idx - b.idx : a.num - b.num);

        // idx 表示区间的右端点

        int idx;

        for (idx = 0; idx <= nums.length - k; idx++) {

            heap.add(new Number(nums[idx], idx));

        }

        int[] res = new int[k];

        int latestIdx = -1;

        for (int i = 0; i < k; i++) {

            Number num = heap.poll();

            // 利用 latestIdx 移动区间左端点

            while (num.idx < latestIdx) {

                num = heap.poll();

            }

            // 取到了当前区间 [x, n - k + i] 中的最小值

            res[i] = num.num;

            latestIdx = num.idx;

            // 右端点更新, 继续向优先队列里加数

            for (; idx <= nums.length - k + i + 1 && idx < nums.length; idx++) {

                heap.add(new Number(nums[idx], idx));

            }

        }

        return res;

    }

}

No.3 使数组互补的最少操作数

解题思路

核心仍然是枚举: 枚举最终的和是多少. 通过预处理的数据快速计算要使每组数达到这个和, 需要改动多少个数字.

代码展示

class Solution {

    public int minMoves(int[] nums, int limit) {

        int halfLen = nums.length / 2;

        // 提取每组数, 便于后续处理

        int[] smallPart = new int[halfLen];

        int[] bigPart = new int[halfLen];

        for (int i = 0, j = nums.length - 1; i < j; i++, j--) {

            smallPart[i] = Math.min(nums[i], nums[j]);

            bigPart[i] = Math.max(nums[i], nums[j]);

        }

        // 数值数量的前缀和

        int[] smallCntPreSum = new int[limit + 1];

        int[] bigCntPreSum = new int[limit + 1];

        for (int i = 0; i < halfLen; i++) {

            smallCntPreSum[smallPart[i]]++;

            bigCntPreSum[bigPart[i]]++;

        }

        for (int i = 1; i <= limit; i++) {

            smallCntPreSum[i] += smallCntPreSum[i - 1];

            bigCntPreSum[i] += bigCntPreSum[i - 1];

        }

        // 原有的加和数值计数

        int[] sumCnt = new int[limit * 2 + 1];

        for (int i = 0; i < halfLen; i++) {

            sumCnt[smallPart[i] + bigPart[i]]++;

        }

        int res = nums.length;

        for (int sum = 2; sum <= limit + limit; ++sum) {

            // 最终的和为 sum 时, 需要改动 tot 个数

            int tot = halfLen - sumCnt[sum];

            if (sum <= limit)

                tot += halfLen - smallCntPreSum[sum - 1];

            else

                tot += bigCntPreSum[sum - limit - 1];

            res = Math.min(res, tot);

        }

        return res;

    }

}

No.4 数组的最小偏移量

解题思路

每个数都有一定的变化范围, 比如 5 只能变成 5 或 10, 而 8 可以变成 1, 2, 4, 8

双向变化不好处理, 我们可以先将每个数都变成它的最大值的形式: 偶数不变, 奇数可以乘以 2.

然后我们需要做的就是把一些数缩小, 以使得最大值和最小值的差最小.

我们需要缩小的就是最大值——因为只有缩小最大值才能影响结果. 所以, 不断缩小最大值即可.

代码展示

class Solution {

    public int minimumDeviation(int[] nums) {

        TreeSet<Integer> set = new TreeSet<>();

        for (int num : nums) {

            set.add(num % 2 == 0 ? num : num * 2);

        }

        int res = set.last() - set.first();

        while (res > 0 && set.last() % 2 == 0) {

            int max = set.last();

            set.remove(max);

            set.add(max / 2);

            res = Math.min(res, set.last() - set.first());

        }

        return res;

    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值