8038. Minimum Operations to Collect Elements

给你一个正整数数组 nums 和一个整数 k 。

一次操作中,你可以将数组的最后一个元素删除,将该元素添加到一个集合中。

请你返回收集元素 1, 2, ..., k 需要的 最少操作次数

测试样例

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

输出:4

解释:4 次操作后,集合中的元素依次添加了 2 ,4 ,5 和 1 。此时集合中包含元素 1 和 2 ,所以答案为 4 。

解答:按照题意模拟并寻找最小操作次数。

class Solution {
    public int minOperations(List<Integer> nums, int k) {
        int[] arr = new int[nums.size()];
        int offset = 0;
        for (int n : nums) {
            arr[offset++] = n;
        }
        boolean[] isUsed = new boolean[k + 1];
        int time = 0;
        for (int i = arr.length - 1; i >= 0; --i) {
            ++time;
            if (arr[i] < isUsed.length && !isUsed[arr[i]]) {
                isUsed[arr[i]] = true;
                --k;
                if (k == 0) {
                    return time;
                }
            }
        }
        return -1;
    }
}

100032. Minimum Number of Operations to Make Array Empty

给你一个下标从 0 开始的正整数数组 nums 。

你可以对数组执行以下两种操作 任意次 :

  • 从数组中选择 两个 值 相等 的元素,并将它们从数组中 删除 。
  • 从数组中选择 三个 值 相等 的元素,并将它们从数组中 删除 。

请你返回使数组为空的 最少 操作次数,如果无法达成,请返回 -1 。

测试样例:

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

输出:4

解释:

我们可以执行以下操作使数组为空:

  • 对下标为 0 和 3 的元素执行第一种操作,得到 nums = [3,3,2,4,2,3,4] 。
  • 对下标为 2 和 4 的元素执行第一种操作,得到 nums = [3,3,4,3,4] 。
  • 对下标为 0 ,1 和 3 的元素执行第二种操作,得到 nums = [4,4] 。
  • 对下标为 0 和 1 的元素执行第一种操作,得到 nums = [] 。

至少需要 4 步操作使数组为空。

解答: 首先利用HashMap将所有相同数字聚合,并且找到每个数字的出现个数。如果有存在出现次数为1的数字,则返回-1.否则利用2和3的整除情况,计算最小次数。

class Solution {
    public int minOperations(int[] nums) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int n : nums) {
            map.put(n, map.getOrDefault(n, 0) + 1);
        }

        int res = 0;
        System.out.println(map);
        for (Map.Entry<Integer, Integer> kv : map.entrySet()) {
            int val = kv.getValue();
            if (val == 1) {
                return -1;
            }
            int div = val / 3, mod = val % 3;
            switch (mod) {
                case 0:
                    res += div;
                    break;
                case 1:
                    div -= 1;
                    res += div;
                    res += 2;
                    break;
                case 2:
                    res += div;
                    res += 1;
            }
        }
        return res;
    }
}

100019. Split Array Into Maximum Number of Subarrays

给你一个只包含 非负 整数的数组 nums 。

我们定义满足 l <= r 的子数组 nums[l..r] 的分数为 nums[l] AND nums[l + 1] AND ... AND nums[r] ,其中 AND 是按位与运算。

请你将数组分割成一个或者更多子数组,满足:

  • 每个 元素都 只 属于一个子数组。
  • 子数组分数之和尽可能 小 。

请你在满足以上要求的条件下,返回 最多 可以得到多少个子数组。

一个 子数组 是一个数组中一段连续的元素。

测试样例:

输入:nums = [5,7,1,3]

输出:1

解释:

我们可以将数组分割成一个子数组:[5,7,1,3] ,分数为 1 ,这是可以得到的最小总分数。在总分数为 1 的前提下,最多可以将数组分割成 1 个子数组。所以返回 1 。

解答: 如果整个数组取AND结果不为0,那么返回1 (按照AND的要求,更短的子数组是没办法获取比最后结果更小的结果)。如果是0,那么从队尾开始遍历数组,寻找到第一个能使得后缀数组取AND为0的位置。最后从头开始遍历数组,并且贪婪地(AND性质,子数组越长,那么取AND结果是0的可能越大。)寻找最大可能情况。

class Solution {
    public int maxSubarrays(int[] nums) {
        int sumAnd = nums[0];
        for (int n : nums) {
            sumAnd &= n;
        }
        if (sumAnd != 0) {
            return 1;
        }
        sumAnd = nums[nums.length - 1];
        int firstZero = nums.length - 1;
        for (int i = nums.length - 1; i >= 0; --i) {
            sumAnd &= nums[i];
            if (sumAnd == 0) {
                firstZero = i;
                break;
            }
        }
        sumAnd = nums[0];
        int res = 0;
        for (int i = 0; i < nums.length; ++i) {
            sumAnd &= nums[i];
            if (sumAnd == 0) {
                ++res;
                if (i + 1 <= firstZero) {
                    sumAnd = nums[i + 1];
                } else {
                    break;
                }
            }
        }
        return res;
    }
}

8051. Maximum Number of K-Divisible Components

给你一棵 n 个节点的无向树,节点编号为 0 到 n - 1 。给你整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 有一条边。

同时给你一个下标从 0 开始长度为 n 的整数数组 values ,其中 values[i] 是第 i 个节点的 值 。再给你一个整数 k 。

你可以从树中删除一些边,也可以一条边也不删,得到若干连通块。一个 连通块的值 定义为连通块中所有节点值之和。如果所有连通块的值都可以被 k 整除,那么我们说这是一个 合法分割 。

请你返回所有合法分割中,连通块数目的最大值 。

测试样例:

输入:n = 5, edges = [[0,2],[1,2],[1,3],[2,4]], values = [1,8,1,4,4], k = 6

输出:2

解释:

我们删除节点 1 和 2 之间的边。这是一个合法分割,因为:

  • 节点 1 和 3 所在连通块的值为 values[1] + values[3] = 12 。
  • 节点 0 ,2 和 4 所在连通块的值为 values[0] + values[2] + values[4] = 6 。

最多可以得到 2 个连通块的合法分割。

解答: 这道题目难度不大。我们其实需要寻找的是一些边,去除这些边之后,分裂的左右两侧子树仍然被k整除。

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

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

        this.values = values;
        this.k = k;
        int[] res = {0};
        dfs(0, -1, res);
        return res[0];
    }

    private int dfs(int cur, int pre, int[] res) {
        int sum = values[cur] % k;
        for (int n : edges[cur]) {
            if (n != pre) {
                sum = (sum + dfs(n, cur, res)) % k;
            }
        }
        if (sum == 0) {
            res[0] += 1;
        }
        return sum;
    }
}

Leave a Reply

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