给你两个字符串 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);
}
}
有一个无限大的二维平面。
给你一个正整数 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
具体实现思路如下:
- 创建一个最大堆
maxHeap
,用来维护距离原点最近的k
个障碍物。 - 遍历每一个查询,对于每一个新的障碍物,计算它到原点的曼哈顿距离
|x| + |y|
。 - 将这个距离加入
maxHeap
,如果maxHeap
的大小超过k
,则移除堆顶元素(即当前最大的距离)。 - 如果
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;
}
}
给你一个由正整数构成的二维矩阵 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; // 记忆化
}
}
给你一个由 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;
}
}