欢迎大家加QQ群:623375442,可以方便群里面交流。

100484. Minimum Positive Sum Subarray

给你一个整数数组 nums 和 两个 整数 l 和 r。你的任务是找到一个长度在 l 和 r 之间(包含)且和大于 0 的 子数组 的 最小 和。

返回满足条件的子数组的 最小 和。如果不存在这样的子数组,则返回 -1。

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

测试样例:

输入:nums = [3, -2, 1, 4], l = 2, r = 3

输出:1

解释:长度在 l = 2 和 r = 3 之间且和大于 0 的子数组有:

  • [3, -2] 和为 1
  • [1, 4] 和为 5
  • [3, -2, 1] 和为 2
  • [-2, 1, 4] 和为 3

其中,子数组 [3, -2] 的和为 1,是所有正和中最小的。因此,答案为 1。

解答:范围不大,直接暴力枚举。

class Solution {
    public int minimumSumSubarray(List<Integer> _nums, int l, int r) {
        int[] nums = new int[_nums.size()];
        int o = 0;
        for (int n : _nums) {
            nums[o++] = n;
        }
        int res = Integer.MAX_VALUE;
        for (int st = 0; st < nums.length; ++st) {
            int ed = st, d = 0, sum = 0;
            while (ed < nums.length && d < r) {
                ++d;
                sum += nums[ed++];
                if (d >= l && d <= r && sum > 0) {
                    res = Math.min(res, sum);
                }
            }
        }
        return res == Integer.MAX_VALUE ? -1 : res;
    }
}

100445. Rearrange K Substrings to Form Target String

给你两个字符串 s 和 t(它们互为字母异位词),以及一个整数 k。

你的任务是判断是否可以将字符串 s 分割成 k 个等长的子字符串,然后重新排列这些子字符串,并以任意顺序连接它们,使得最终得到的新字符串与给定的字符串 t 相匹配。

如果可以做到,返回 true;否则,返回 false。

字母异位词 是指由另一个单词或短语的所有字母重新排列形成的单词或短语,使用所有原始字母恰好一次。

子字符串 是字符串中的一个连续 非空 字符序列。

测试样例:

输入:s = "abcd", t = "cdab", k = 2

输出:true

解释:

  • 将 s 分割成 2 个长度为 2 的子字符串:["ab", "cd"]。
  • 重新排列这些子字符串为 ["cd", "ab"],然后连接它们得到 "cdab",与 t 相匹配。

解答:利用哈希表储存一下s拆分后的字符串,然后相同逻辑拆分一下t,看看能不能组装上。

class Solution {
    public boolean isPossibleToRearrange(String s, String t, int k) {
        HashMap<String, Integer> map = new HashMap<>();
        int j = s.length() / k;
        for (int i = 0; i < s.length(); i = i + j) {
            String sub = s.substring(i, i + j);
            map.put(sub, map.getOrDefault(sub, 0) + 1);
        }
        for (int i = 0; i < t.length(); i = i + j) {
            String sub = t.substring(i, i + j);
            int c = map.getOrDefault(sub, 0);
            if (c == 0) {
                return false;
            }
            map.put(sub, c - 1);
        }
        return true;
    }
}

100493. Minimum Array Sum

给你一个整数数组 nums 和三个整数 k、op1 和 op2。

你可以对 nums 执行以下操作:

  • 操作 1:选择一个下标 i,将 nums[i] 除以 2,并 向上取整 到最接近的整数。你最多可以执行此操作 op1 次,并且每个下标最多只能执行一次。
  • 操作 2:选择一个下标 i,仅当 nums[i] 大于或等于 k 时,从 nums[i] 中减去 k。你最多可以执行此操作 op2 次,并且每个下标最多只能执行一次。

注意: 两种操作可以应用于同一下标,但每种操作最多只能应用一次。

返回在执行任意次数的操作后,nums 中所有元素的 最小 可能 和 。

测试样例:

输入:nums = [2,8,3,19,3], k = 3, op1 = 1, op2 = 1

输出:23

解释:

  • 对 nums[1] = 8 应用操作 2,使 nums[1] = 5。
  • 对 nums[3] = 19 应用操作 1,使 nums[3] = 10。
  • 结果数组变为 [2, 5, 3, 10, 3],在应用操作后具有最小可能和 23。

解答:这题范围太小了。nums.length <= 100。每一种操作对于每一个下标也只能执行一次。利用一下动态规划的思维,快速计算。

class Solution {
    public int minArraySum(int[] nums, int k, int op1, int op2) {
        Integer[][] dp = new Integer[op1 + 1][op2 + 1];
        dp[op1][op2] = 0;
        for (int n : nums) {
            Integer[][] nextDp = new Integer[op1 + 1][op2 + 1];
            for (int i = 0; i <= op1; ++i) {
                for (int j = 0; j <= op2; ++j) {
                    if (dp[i][j] == null) continue;
                    // 枚举一下可能
                    // 1. 不执行
                    // 2. 执行操作1
                    // 3. 执行操作2
                    // 4. 执行操作1和操作2
                    nextDp[i][j] = min(nextDp[i][j], dp[i][j] + n);
                    if (i >= 1) {
                        nextDp[i - 1][j] = min(nextDp[i - 1][j], dp[i][j] + round(n));
                    }
                    if (j >= 1) {
                        nextDp[i][j - 1] = min(nextDp[i][j - 1], dp[i][j] + remove(n, k));
                    }
                    if (i >= 1 && j >= 1) {
                        nextDp[i - 1][j - 1] = min(nextDp[i - 1][j - 1],
                                dp[i][j] + Math.min(remove(round(n), k), round(remove(n, k))));
                    }
                }
            }
            dp = nextDp;
        }
        int res = Integer.MAX_VALUE;
        for (Integer[] r : dp) {
            for (Integer n : r) {
                res = min(n, res);
            }
        }
        return res;
    }

    private Integer min(Integer cur, int next) {
        if (cur == null) {
            return next;
        } else if (cur <= next) {
            return cur;
        } else {
            return next;
        }
    }

    private int round(int n) {
        return n / 2 + n % 2;
    }

    private int remove(int n, int k) {
        if (n >= k) {
            return n - k;
        } else {
            return n;
        }
    }
}

100500. Maximize Sum of Weights after Edge Removals

存在一棵具有 n 个节点的无向树,节点编号为 0 到 n - 1。给你一个长度为 n - 1 的二维整数数组 edges,其中 edges[i] = [ui, vi, wi] 表示在树中节点 ui 和 vi 之间有一条权重为 wi 的边。

你的任务是移除零条或多条边,使得:

  • 每个节点与至多 k 个其他节点有边直接相连,其中 k 是给定的输入。
  • 剩余边的权重之和 最大化 。

返回在进行必要的移除后,剩余边的权重的 最大 可能和。

测试样例:

输入:edges = [[0,1,4],[0,2,2],[2,3,12],[2,4,6]], k = 2

输出:22

解释:

  • 节点 2 与其他 3 个节点相连。我们移除边 [0, 2, 2],确保没有节点与超过 k = 2 个节点相连。
  • 权重之和为 22,无法获得更大的和。因此,答案是 22。

解答:也有一点动态规划的逻辑在。我们假设0是根结点。每个节点分成2种可能:一种是与父节点断开(这个时候最多k个子节点),另一种是与父节点连着(这个时候最多k-1个子节点)。那遍历的逻辑就比较简单的了。对于每个节点,类和一下子节点全部断开的场景。然后选择(k - 1) or (k) 个最优节点连接上。

class Solution {
    private class Data {
        List<int[]>[] edges;
        int k;

        public Data(int[][] _edges, int k) {
            this.k = k;
            int n = _edges.length + 1;
            edges = new List[n];
            for (int i = 0; i < edges.length; ++i) {
                edges[i] = new ArrayList<>();
            }
            for (int[] e : _edges) {
                edges[e[0]].add(new int[]{e[1], e[2]});
                edges[e[1]].add(new int[]{e[0], e[2]});
            }
        }
    }

    private static final long[] empty = new long[2];

    public long maximizeSumOfWeights(int[][] edges, int k) {
        Data data = new Data(edges, k);
        return helper(0, -1, data)[0];
    }

    private long[] helper(int node, int pre, Data data) {
        PriorityQueue<Long> topDiff = new PriorityQueue<>();
        long disconnectSum = 0;
        for (int[] next : data.edges[node]) {
            if (next[0] != pre) {
                long[] tmp = helper(next[0], node, data);
                // 假设与所有子节点全部断开
                disconnectSum += tmp[0];
                // 寻找到最大的k个链接奖励
                long connectBenefit = next[1] + tmp[1] - tmp[0];
                topDiff.add(connectBenefit);
                if (topDiff.size() > data.k) {
                    topDiff.poll();
                }
            }
        }
        long[] res = {disconnectSum, disconnectSum};
        if (!topDiff.isEmpty()) {
            if (topDiff.size() == data.k) {
                // 如果链接带来的收益小于0,那就不链接
                res[0] += Math.max(topDiff.poll(), 0);
            }
            for (long n : topDiff) {
                res[0] += Math.max(n, 0);
                res[1] += Math.max(n, 0);
            }
        }
        return res;
    }
}

Leave a Reply

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