力扣第 413 场周赛

3274. 检查棋盘方格颜色是否相同

给你两个字符串 coordinate1 和 coordinate2,代表 8 x 8 国际象棋棋盘上的两个方格的坐标。

以下是棋盘的参考图。

如果这两个方格颜色相同,返回 true,否则返回 false

坐标总是表示有效的棋盘方格。坐标的格式总是先字母(表示列),再数字(表示行)。

示例 1:

输入: coordinate1 = "a1", coordinate2 = "c3"

输出: true

解释:

两个方格均为黑色。

示例 2:

输入: coordinate1 = "a1", coordinate2 = "h3"

输出: false

解释:

方格 "a1" 是黑色,而 "h3" 是白色。

提示:

  • coordinate1.length == coordinate2.length == 2
  • 'a' <= coordinate1[0], coordinate2[0] <= 'h'
  • '1' <= coordinate1[1], coordinate2[1] <= '8'

思路:看代码即可,判断两个坐标的和是否同为偶数或同为奇数

class Solution {
    public boolean checkTwoChessboards(String coordinate1, String coordinate2) {
           // 将列的字母转换为数字,再加上行的数字
    int sum1 = (coordinate1.charAt(0) - 'a') + (coordinate1.charAt(1) - '0');
    int sum2 = (coordinate2.charAt(0) - 'a') + (coordinate2.charAt(1) - '0');

    // 判断两个坐标的和是否同为偶数或同为奇数
    return (sum1 % 2) == (sum2 % 2);
    }
}

3275. 第 K 近障碍物查询

有一个无限大的二维平面。

给你一个正整数 k ,同时给你一个二维数组 queries ,包含一系列查询:

  • queries[i] = [x, y] :在平面上坐标 (x, y) 处建一个障碍物,数据保证之前的查询 不会 在这个坐标处建立任何障碍物。

每次查询后,你需要找到离原点第 k  障碍物到原点的 距离 。

请你返回一个整数数组 results ,其中 results[i] 表示建立第 i 个障碍物以后,离原地第 k 近障碍物距离原点的距离。如果少于 k 个障碍物,results[i] == -1 。

注意,一开始 没有 任何障碍物。

坐标在 (x, y) 处的点距离原点的距离定义为 |x| + |y| 。

示例 1:

输入:queries = [[1,2],[3,4],[2,3],[-3,0]], k = 2

输出:[-1,7,5,3]

解释:

最初,不存在障碍物。

  • queries[0] 之后,少于 2 个障碍物。
  • queries[1] 之后, 两个障碍物距离原点的距离分别为 3 和 7 。
  • queries[2] 之后,障碍物距离原点的距离分别为 3 ,5 和 7 。
  • queries[3] 之后,障碍物距离原点的距离分别为 3,3,5 和 7 。

示例 2:

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

输出:[10,8,6]

解释:

  • queries[0] 之后,只有一个障碍物,距离原点距离为 10 。
  • queries[1] 之后,障碍物距离原点距离分别为 8 和 10 。
  • queries[2] 之后,障碍物距离原点的距离分别为 6, 8 和10 。

提示:

  • 1 <= queries.length <= 2 * 10^5
  • 所有 queries[i] 互不相同。
  • -109 <= queries[i][0], queries[i][1] <= 10^9
  • 1 <= k <= 10^5

具体实现思路如下:

  1. 创建一个最大堆 maxHeap,用来维护距离原点最近的 k 个障碍物。
  2. 遍历每一个查询,对于每一个新的障碍物,计算它到原点的曼哈顿距离 |x| + |y|
  3. 将这个距离加入 maxHeap,如果 maxHeap 的大小超过 k,则移除堆顶元素(即当前最大的距离)。
  4. 如果 maxHeap 的大小等于 k,则堆顶的元素就是第 k 近的障碍物到原点的距离,否则返回 -1
class Solution {
    public int[] resultsArray(int[][] queries, int k) {
             // 使用优先级队列作为最大堆
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
        int[] results = new int[queries.length];

        for (int i = 0; i < queries.length; i++) {
            int x = queries[i][0];
            int y = queries[i][1];
            int distance = Math.abs(x) + Math.abs(y); // 计算曼哈顿距离

            maxHeap.offer(distance); // 添加当前距离到堆中

            // 如果堆的大小超过k,则移除最大的距离
            if (maxHeap.size() > k) {
                maxHeap.poll();
            }

            // 如果堆的大小等于k,则堆顶元素即为第k近的距离
            if (maxHeap.size() == k) {
                results[i] = maxHeap.peek();
            } else {
                results[i] = -1; // 如果堆中元素不足k个,则返回-1
            }
        }

        return results;

    }
}

3276. 选择矩阵中单元格的最大得分

给你一个由正整数构成的二维矩阵 grid

你需要从矩阵中选择 一个或多个 单元格,选中的单元格应满足以下条件:

  • 所选单元格中的任意两个单元格都不会处于矩阵的 同一行
  • 所选单元格的值 互不相同

你的得分为所选单元格值的总和

返回你能获得的 最大 得分。

示例 1:

输入: grid = [[1,2,3],[4,3,2],[1,1,1]]

输出: 8

解释:

选择上图中用彩色标记的单元格,对应的值分别为 1、3 和 4 。

示例 2:

输入: grid = [[8,7,6],[8,3,2]]

输出: 15

解释:

选择上图中用彩色标记的单元格,对应的值分别为 7 和 8 。

提示:

  • 1 <= grid.length, grid[i].length <= 10
  • 1 <= grid[i][j] <= 100

思路:(自己不会,参考灵神的)看示例 1,grid 的最大值为 4。

讨论 4 选或不选:

不选 4,问题变成在 [1,3] 中选数字,每行至多一个数且没有相同元素时,所选元素之和的最大值。
选 4,枚举选哪一行的 4(本例中只能选第二行),问题变成在 [1,3] 中选数字,第二行不能选数字,每行至多一个数且没有相同元素时,所选元素之和的最大值。
于是,我们需要在递归的过程中,跟踪两个信息:

i:当前要在 [1,i] 中选数字。
j:已选的行号集合为 j。后续所选数字所在的行号不能在 j 中。
因此,定义状态为 dfs(i,j),表示在 [1,i] 中选数字,所选数字所处的行号不能在集合 j 中,每行至多一个数且没有相同元素时,所选元素之和的最大值。

讨论元素 i 选或不选:

不选 i,问题变成在 [1,i−1] 中选数字,所选数字所处的行号不能在集合 j 中,每行至多一个数且没有相同元素时,所选元素之和的最大值,即 dfs(i−1,j)。
选 i,枚举选第 k 行的 i(提前预处理 i 所处的行号),问题变成在 [1,i−1] 中选数字(注意 i 只能选一次),所选数字所处的行号不能在集合 j∪{k} 中,每行至多一个数且没有相同元素时,所选元素之和的最大值,即 dfs(i−1,j∪{k})。
两种情况取最大值,即为 dfs(i,j)。

递归边界:dfs(0,j)=0。

递归入口:dfs(mx,∅)。一开始没有选行号。

代码:

class Solution {
    public int maxScore(List<List<Integer>> grid) {
        Map<Integer, Integer> pos = new HashMap<>();
        int m = grid.size();
        for (int i = 0; i < m; i++) {
            for (int x : grid.get(i)) {
                // 保存所有包含 x 的行号(压缩成二进制数)
                pos.merge(x, 1 << i, (a, b) -> a | b);
            }
        }

        List<Integer> allNums = new ArrayList<>(pos.keySet());
        int n = allNums.size();
        int[][] memo = new int[n][1 << m];
        for (int[] row : memo) {
            Arrays.fill(row, -1); // -1 表示没有计算过
        }
        return dfs(n - 1, 0, pos, allNums, memo);
    }

    private int dfs(int i, int j, Map<Integer, Integer> pos, List<Integer> allNums, int[][] memo) {
        if (i < 0) {
            return 0;
        }
        if (memo[i][j] != -1) { // 之前计算过
            return memo[i][j];
        }
        // 不选 x
        int res = dfs(i - 1, j, pos, allNums, memo);
        // 枚举选第 k 行的 x
        int x = allNums.get(i);
        for (int t = pos.get(x), lb; t > 0; t ^= lb) {
            lb = t & -t; // lb = 1<<k,其中 k 是行号
            if ((j & lb) == 0) { // 没选过第 k 行的数
                res = Math.max(res, dfs(i - 1, j | lb, pos, allNums, memo) + x);
            }
        }
        return memo[i][j] = res; // 记忆化
    }
}

3277. 查询子数组最大异或值

给你一个由 n 个整数组成的数组 nums,以及一个大小为 q 的二维整数数组 queries,其中 queries[i] = [li, ri]

对于每一个查询,你需要找出 nums[li..ri] 中任意 子数组的 最大异或值

数组的异或值 需要对数组 a 反复执行以下操作,直到只剩一个元素,剩下的那个元素就是 异或值

  • 对于除最后一个下标以外的所有下标 i,同时将 a[i] 替换为 a[i] XOR a[i + 1] 。
  • 移除数组的最后一个元素。

返回一个大小为 q 的数组 answer,其中 answer[i] 表示查询 i 的答案。

示例 1:

输入: nums = [2,8,4,32,16,1], queries = [[0,2],[1,4],[0,5]]

输出: [12,60,60]

解释:

在第一个查询中,nums[0..2] 的子数组分别是 [2][8][4][2, 8][8, 4], 和 [2, 8, 4],它们的异或值分别为 2, 8, 4, 10, 12, 和 6。查询的答案是 12,所有异或值中的最大值。

在第二个查询中,nums[1..4] 的子数组中最大的异或值是子数组 nums[1..4] 的异或值,为 60。

在第三个查询中,nums[0..5] 的子数组中最大的异或值是子数组 nums[1..4] 的异或值,为 60。

示例 2:

输入: nums = [0,7,3,2,8,5,1], queries = [[0,3],[1,5],[2,4],[2,6],[5,6]]

输出: [7,14,11,14,5]

解释:

下标nums[li..ri]最大异或值子数组子数组最大异或值
0[0, 7, 3, 2][7]7
1[7, 3, 2, 8, 5][7, 3, 2, 8]14
2[3, 2, 8][3, 2, 8]11
3[3, 2, 8, 5, 1][2, 8, 5, 1]14
4[5, 1][5]5

提示:

  • 1 <= n == nums.length <= 2000
  • 0 <= nums[i] <= 2^31 - 1
  • 1 <= q == queries.length <= 10^5
  • queries[i].length == 2
  • queries[i] = [li, ri]
  • 0 <= li <= ri <= n - 1

思路:数组的异或值,下文用 ⊕ 表示异或。

考虑数组的异或值(最后剩下的元素)是由哪些元素异或得到的。

例如数组为 [a,b,c],那么操作一次后变成 [a⊕b, b⊕c],再操作一次,得到 a⊕b⊕b⊕c。其中 b 异或了 2 次。

为方便描述,下面把 a⊕b 简记为 ab,把 a⊕b⊕b⊕c 简记为 ab^2c.

又例如数组为 [a,b,c,d],那么操作一次后变成 [ab,bc,cd],再操作一次,变成 [ab^2c,bc^2d],再操作一次,得到 ab^3c^3d。

可以发现,ab^3c^3d 相当于数组 [a,b,c] 的异或值,再异或 [b,c,d] 的异或值。

第一个区间 DP
定义 f[i][j] 表示下标从 i 到 j 的子数组的「数组的异或值」,根据上面的讨论,有

                                                f[i][j]=f[i][j−1]⊕f[i+1][j]
初始值:f[i][i]=nums[i]。

第二个区间 DP
为了回答询问,我们需要计算下标从 i 到 j 的子数组中的所有子数组的 f 值的最大值,将其记作 mx[i][j]。

分类讨论:

取 f[i][j] 作为最大值。
最大值对应的子数组,右端点不是 j,那么问题变成下标从 i 到 j−1 的子数组中的所有子数组的 f 值的最大值,即 mx[i][j−1]。
最大值对应的子数组,左端点不是 i,那么问题变成下标从 i+1 到 j 的子数组中的所有子数组的 f 值的最大值,即 mx[i+1][j]。
三者取最大值,得

                                mx[i][j]=max(f[i][j],mx[i][j−1],mx[i+1][j])
初始值:mx[i][i]=nums[i]。

代码:

class Solution {
    public int[] maximumSubarrayXor(int[] nums, int[][] queries) {
        int n = nums.length;
        int[][] f = new int[n][n];
        int[][] mx = new int[n][n];
        for (int i = n - 1; i >= 0; i--) {
            //初始值可以看下它们各自的定义即可
            f[i][i] = nums[i];
            mx[i][i] = nums[i];
            for (int j = i + 1; j < n; j++) {
                f[i][j] = f[i][j - 1] ^ f[i + 1][j];
                mx[i][j] = Math.max(f[i][j], Math.max(mx[i + 1][j], mx[i][j - 1]));
            }
        }

        int[] ans = new int[queries.length];
        for (int i = 0; i < queries.length; i++) {
            ans[i] = mx[queries[i][0]][queries[i][1]];
        }
        return ans;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值