100243. Distribute Elements Into Two Arrays I

给你一个下标从 1 开始、包含 不同 整数的数组 nums ,数组长度为 n 。

你需要通过 n 次操作,将 nums 中的所有元素分配到两个数组 arr1 和 arr2 中。在第一次操作中,将 nums[1] 追加到 arr1 。在第二次操作中,将 nums[2] 追加到 arr2 。之后,在第 i 次操作中:

  • 如果 arr1 的最后一个元素 大于 arr2 的最后一个元素,就将 nums[i] 追加到 arr1 。否则,将 nums[i] 追加到 arr2 。

通过连接数组 arr1 和 arr2 形成数组 result 。例如,如果 arr1 == [1,2,3] 且 arr2 == [4,5,6] ,那么 result = [1,2,3,4,5,6] 。

返回数组 result 。

测试样例:

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

输出:[2,3,1]

解释:在前两次操作后,arr1 = [2] ,arr2 = [1] 。在第 3 次操作中,由于 arr1 的最后一个元素大于 arr2 的最后一个元素(2 > 1),将 nums[3] 追加到 arr1 。3 次操作后,arr1 = [2,3] ,arr2 = [1] 。因此,连接形成的数组 result 是 [2,3,1] 。

解答:创建两个队列,按照要求对比队列元素大小完成数组更新。

class Solution {
    public int[] resultArray(int[] nums) {
        List<Integer> arr1 = new ArrayList<>(), arr2 = new ArrayList<>();
        arr1.add(nums[0]); arr2.add(nums[1]);
        for (int i = 2; i < nums.length; ++i) {
            if (arr1.get(arr1.size() - 1) > arr2.get(arr2.size() - 1)) {
                arr1.add(nums[i]);
            } else {
                arr2.add(nums[i]);
            }
        }
        int offset = 0;
        for (int n : arr1) {
            nums[offset++] = n;
        }
        for (int n : arr2) {
            nums[offset++] = n;
        }
        return nums;
    }
}

100237. Count Submatrices with Top-Left Element and Sum Less Than k

给你一个下标从 0 开始的整数矩阵 grid 和一个整数 k。

返回包含 grid 左上角元素、元素和小于或等于 k 的 子矩阵 的数目。

测试样例:

输入:grid = [[7,6,3],[6,6,1]], k = 18

输出:4

解释:只有 4 个子矩阵满足:包含 grid 的左上角元素,并且元素和小于或等于 18 。

解答:二维矩阵上的增强版前缀和。我们将每一列的前缀和在遍历的时候记录下。那么按行遍历的时候就能获取左上角累和情况。

class Solution {
    public int countSubmatrices(int[][] grid, int k) {
        int m = grid.length, n = grid[0].length;
        int res = 0;
        int[] sum = new int[n];
        for (int i = 0; i < m; ++i) {
            int[] row = grid[i];
            int last = 0;
            for (int j = 0; j < n; ++j) {
                sum[j] += row[j];
                last += sum[j];
                if (last <= k) {
                    ++res;
                }
            }
        }
        return res;
    }
}

100234. Minimum Operations to Write the Letter Y on a Grid

给你一个下标从 0 开始、大小为 n x n 的矩阵 grid ,其中 n 为奇数,且 grid[r][c] 的值为 0 、1 或 2 。

如果一个单元格属于以下三条线中的任一一条,我们就认为它是字母 Y 的一部分:

  • 从左上角单元格开始到矩阵中心单元格结束的对角线。
  • 从右上角单元格开始到矩阵中心单元格结束的对角线。
  • 从中心单元格开始到矩阵底部边界结束的垂直线。

当且仅当满足以下全部条件时,可以判定矩阵上写有字母 Y :

  • 属于 Y 的所有单元格的值相等。
  • 不属于 Y 的所有单元格的值相等。
  • 属于 Y 的单元格的值与不属于Y的单元格的值不同。

每次操作你可以将任意单元格的值改变为 0 、1 或 2 。返回在矩阵上写出字母 Y 所需的 最少 操作次数。

测试样例:

输入:grid = [[0,1,0,1,0],[2,1,0,1,2],[2,2,2,0,1],[2,2,2,2,2],[2,1,2,2,2]]

输出:12

解释:将在矩阵上写出字母 Y 需要执行的操作用蓝色高亮显示。操作后,所有属于 Y 的单元格(加粗显示)的值都为 0 ,而不属于 Y 的单元格的值都为 2 。
可以证明,写出 Y 至少需要进行 12 次操作。

解答:按照题意,分解出属于Y的元素和不属于Y的元素。暴力枚举Y和非Y元素的统一值,寻找最小值。

class Solution {
    public int minimumOperationsToWriteY(int[][] grid) {
        int[] yc = new int[3], oc = new int[3];
        int n = grid.length;
        int mid = n / 2;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (i == j && i <= mid) {
                    yc[grid[i][j]] += 1;
                } else if (i + j == n - 1 && i <= mid) {
                    yc[grid[i][j]] += 1;
                } else if (mid == j && i > mid) {
                    yc[grid[i][j]] += 1;
                } else {
                    oc[grid[i][j]] += 1;
                }
            }
        }
        int res = Integer.MAX_VALUE;
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                if (i != j) {
                    res = Math.min(res, differCount(yc, i) + differCount(oc, j));
                }
            }
        }
        return res;
    }

    private int differCount(int[] arr, int target) {
        int res = 0;
        for (int i = 0; i < arr.length; ++i) {
            if (i != target) res += arr[i];
        }
        return res;
    }
}

100246. Distribute Elements Into Two Arrays II

给你一个下标从 1 开始、长度为 n 的整数数组 nums 。

现定义函数 greaterCount ,使得 greaterCount(arr, val) 返回数组 arr 中 严格大于 val 的元素数量。

你需要使用 n 次操作,将 nums 的所有元素分配到两个数组 arr1 和 arr2 中。在第一次操作中,将 nums[1] 追加到 arr1 。在第二次操作中,将 nums[2] 追加到 arr2 。之后,在第 i 次操作中:

  • 如果 greaterCount(arr1, nums[i]) > greaterCount(arr2, nums[i]) ,将 nums[i] 追加到 arr1 。
  • 如果 greaterCount(arr1, nums[i]) < greaterCount(arr2, nums[i]) ,将 nums[i] 追加到 arr2 。
  • 如果 greaterCount(arr1, nums[i]) == greaterCount(arr2, nums[i]) ,将 nums[i] 追加到元素数量较少的数组中。
  • 如果仍然相等,那么将 nums[i] 追加到 arr1 。

连接数组 arr1 和 arr2 形成数组 result 。例如,如果 arr1 == [1,2,3] 且 arr2 == [4,5,6] ,那么 result = [1,2,3,4,5,6] 。

返回整数数组 result 。

测试样例:

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

输出:[2,3,1,3]

解释:在前两次操作后,arr1 = [2] ,arr2 = [1] 。在第 3 次操作中,两个数组中大于 3 的元素数量都是零,并且长度相等,因此,将 nums[3] 追加到 arr1 。在第 4 次操作中,两个数组中大于 3 的元素数量都是零,但 arr2 的长度较小,因此,将 nums[4] 追加到 arr2 。在 4 次操作后,arr1 = [2,3] ,arr2 = [1,3] 。因此,连接形成的数组 result 是 [2,3,1,3] 。

解答:这题难度不大,按照题目要求完成操作即可。难点是计算greaterCount函数,可以利用线段树储存数字分布情况,然后快速计算greaterCount函数。

class Solution {
    class SegmentTree {
        Map<Integer, Integer> sortMap;
        List<Integer> buffer;
        int[] tree;
        int n, size;

        public SegmentTree(Map<Integer, Integer> _sortMap) {
            sortMap = _sortMap;
            n = sortMap.size();
            tree = new int[n * 2];
            size = 0;
            buffer = new ArrayList<>();
        }

        public int greaterCount(int val) {
            int offset = sortMap.get(val) + 1;
            return sumRange(offset, n - 1);
        }

        public void addNum(int val) {
            buffer.add(val);
            int offset = sortMap.get(val);
            ++size;
            update(offset);
        }

        private void update(int pos) {
            pos += n;
            tree[pos] += 1;
            while (pos > 0) {
                int left = pos;
                int right = pos;
                if (pos % 2 == 0) {
                    right = pos + 1;
                } else {
                    left = pos - 1;
                }
                tree[pos / 2] = tree[left] + tree[right];
                pos /= 2;
            }
        }

        private int sumRange(int l, int r) {
            l += n;
            r += n;
            int sum = 0;
            while (l <= r) {
                if ((l % 2) == 1) {
                    sum += tree[l];
                    l++;
                }
                if ((r % 2) == 0) {
                    sum += tree[r];
                    r--;
                }
                l /= 2;
                r /= 2;
            }
            return sum;
        }
    }

    public int[] resultArray(int[] nums) {
        Map<Integer, Integer> sortMap = sortDistinctNum(nums);
        SegmentTree arr1 = new SegmentTree(sortMap), arr2 = new SegmentTree(sortMap);
        arr1.addNum(nums[0]);
        arr2.addNum(nums[1]);
        for (int i = 2; i < nums.length; ++i) {
            int g1 = arr1.greaterCount(nums[i]), g2 = arr2.greaterCount(nums[i]);
            if (g1 > g2) {
                arr1.addNum(nums[i]);
            } else if (g1 < g2) {
                arr2.addNum(nums[i]);
            } else {
                if (arr1.size <= arr2.size) {
                    arr1.addNum(nums[i]);
                } else {
                    arr2.addNum(nums[i]);
                }
            }
        }
        int[] res = new int[nums.length];
        int o = 0;
        for (int i : arr1.buffer) {
            res[o++] = i;
        }
        for (int i : arr2.buffer) {
            res[o++] = i;
        }
        return res;
    }

    private Map<Integer, Integer> sortDistinctNum(int[] nums) {
        TreeSet<Integer> set = new TreeSet<>();
        for (int n : nums) {
            set.add(n);

        }
        int offset = 0;
        HashMap<Integer, Integer> res = new HashMap<>();
        for (int n : set) {
            res.put(n, offset++);
        }
        return res;
    }
}

Leave a Reply

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