欢迎大家加QQ群:623375442,可以方便群里面交流。这周题目还是有点难度,但是比昨天双周赛那一堆数学操作好。

100548. Sum of Variable Length Subarrays

给你一个长度为 n 的整数数组 nums 。对于 每个 下标 i(0 <= i < n),定义对应的子数组 nums[start ... i](start = max(0, i - nums[i]))。

返回为数组中每个下标定义的子数组中所有元素的总和。

子数组 是数组中的一个连续、非空 的元素序列。

测试样例:

输入:nums = [2,3,1]

输出:11

解释:总和为 11 。因此,输出 11 。

解答:按照题意,暴力计算。

class Solution {
    public int subarraySum(int[] nums) {
        int res = 0;
        for (int i = 0; i < nums.length; ++i) {
            int st = Math.max(0, i - nums[i]);
            for (int j = st; j <= i; ++j) {
                res += nums[j];
            }
        }
        return res;
    }
}

100534. Maximum and Minimum Sums of at Most Size K Subsequences

给你一个整数数组 nums 和一个正整数 k,返回所有长度最多为 k 的 子序列 中 最大值 与 最小值 之和的总和。

非空子序列 是指从另一个数组中删除一些或不删除任何元素(且不改变剩余元素的顺序)得到的数组。

由于答案可能非常大,请返回对 10^9 + 7 取余数的结果。

测试样例:

输入:nums = [1,2,3], k = 2

输出:2

解释:数组 nums 中所有长度最多为 2 的子序列。因此,输出为 24。

解答:没想到啥好办法,利用组合的特性。

class Solution {
    private static final int mod = 1_000_000_007;

    public int minMaxSums(int[] nums, int k) {
        Arrays.sort(nums);
        long res = 0;
        long[] dp = new long[k];
        dp[0] = 1;
        for (int i = 0; i < nums.length; ++i) {
            long sum = 1;
            // 前i - 1个数中挑选0 到 k - 1个数字
            for (int j = Math.min(i, k - 1); j >= 1; --j) {
                dp[j] = (dp[j] + dp[j - 1]) % mod;
                sum += dp[j];
                sum %= mod;
            }
            res += sum * nums[i] % mod;
            res %= mod;
        }

        dp = new long[k + 1];
        dp[0] = 1;
        for (int i = nums.length - 1; i >= 0; --i) {
            long sum = 1;
            for (int j = Math.min(nums.length - i - 1, k - 1); j >= 1; --j) {
                dp[j] = (dp[j] + dp[j - 1]) % mod;
                sum += dp[j];
                sum %= mod;
            }
            res += sum * nums[i] % mod;
            res %= mod;
        }
        return (int) res;
    }
}

100553. Paint House IV

给你一个 偶数 整数 n,表示沿直线排列的房屋数量,以及一个大小为 n x 3 的二维数组 cost,其中 cost[i][j] 表示将第 i 个房屋涂成颜色 j + 1 的成本。

如果房屋满足以下条件,则认为它们看起来 漂亮:

  • 不存在 两个 涂成相同颜色的相邻房屋。
  • 距离行两端 等距 的房屋不能涂成相同的颜色。例如,如果 n = 6,则位置 (0, 5)、(1, 4) 和 (2, 3) 的房屋被认为是等距的。

返回使房屋看起来 漂亮 的 最低 涂色成本。

测试样例:

输入:n = 4, cost = [[3,5,7],[6,2,9],[4,8,1],[7,3,5]]

输出:9

解释:最佳涂色顺序为 [1, 2, 3, 2],对应的成本为 [3, 2, 1, 3]。满足以下条件:

  • 不存在涂成相同颜色的相邻房屋。
  • 位置 0 和 3 的房屋(等距于两端)涂成不同的颜色 (1 != 2)。
  • 位置 1 和 2 的房屋(等距于两端)涂成不同的颜色 (2 != 3)。

使房屋看起来漂亮的最低涂色成本为 3 + 2 + 1 + 3 = 9。

解答:动态规划题。感谢都是偶数。我们把距离两端等距的房屋看成一堆。一共3种颜色,那么2件房间的组合一共是9种(有几种不可行,等距房间不能同色)。同时要注意相邻房间不能同色。

class Solution {
    private static final int[][] map = {{0,0},{0,1},{0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};

    public long minCost(int n, int[][] cost) {
        int st = 0, ed = n - 1;
        long[] dp = new long[9];
        while (st < ed) {
            long[] nextDp = new long[9];
            Arrays.fill(nextDp, Long.MAX_VALUE / 2);
            for (int i = 0; i < 9; ++i) {
                for (int j = 0; j < 9; ++j) {
                    // 动态规划
                    if (isValid(map[i], map[j])) {
                        nextDp[i] = Math.min(nextDp[i], dp[j] + cost[st][map[i][0]] + cost[ed][map[i][1]]);
                    }
                }
            }
            dp = nextDp;
            ++st;
            --ed;
        }
        long res = Long.MAX_VALUE;
        for (long t : dp) {
            res = Math.min(res, t);
        }
        return res;
    }

    private boolean isValid(int[] n1, int[] n2) {
        // 两端房间不能同色 + 相邻不能同色
        return n1[0] != n1[1] && n2[0] != n2[1] && n1[0] != n2[0] && n1[1] != n2[1];
    }
}

100543. Maximum and Minimum Sums of at Most Size K Subarrays

给你一个整数数组 nums 和一个 正 整数 k 。 返回 最多 有 k 个元素的所有子数组的 最大 和 最小 元素之和。

子数组 是数组中的一个连续、非空 的元素序列。

测试样例:

输入:nums = [1,2,3], k = 2

输出:20

解释:最多 2 个元素的 nums 的子数组, 输出为 20 。

解答:利用队列来模拟,查看每个数字影响范围。然后类和。

class Solution {
    public long minMaxSubarraySum(int[] nums, int k) {
        Deque<long[]> min = new LinkedList<>(), max = new LinkedList<>();
        long minSum = 0, maxSum = 0;
        long res = 0;
        for (int i = 0; i < nums.length; ++i) {
            // min的范围
            while (!min.isEmpty() && nums[(int) min.peekLast()[0]] >= nums[i]) {
                minSum -= min.removeLast()[1];
            }
            long[] minAdd = {i, (min.isEmpty() ? ((long) Math.min(i + 1, k)) : (i - min.peekLast()[0])) * nums[i]};
            minSum += minAdd[1];
            min.addLast(minAdd);
            res += minSum;

            // max的范围
            while (!max.isEmpty() && nums[(int) max.peekLast()[0]] <= nums[i]) {
                maxSum -= max.removeLast()[1];
            }
            long[] maxAdd = {i, (max.isEmpty() ? ((long) Math.min(i + 1, k)) : (i - max.peekLast()[0])) * nums[i]};
            maxSum += maxAdd[1];
            max.addLast(maxAdd);
            res += maxSum;

            // 当超过k个元素之后,需要考虑到出栈
            if (i >= k - 1) {
                long[] firstMin = min.getFirst();
                minSum -= nums[(int) firstMin[0]];
                if (firstMin[0] == i - k + 1) {
                    min.removeFirst();
                } else {
                    firstMin[1] -= nums[(int) firstMin[0]];
                }

                long[] firstMax = max.getFirst();
                maxSum -= nums[(int) firstMax[0]];
                if (firstMax[0] == i - k + 1) {
                    max.removeFirst();
                } else {
                    firstMax[1] -= nums[(int) firstMax[0]];
                }
            }
        }
        return res;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *