Leetcode 第 413 场周赛题解
- Leetcode 第 413 场周赛题解
- 题目1:3274. 检查棋盘方格颜色是否相同
- 思路
- 代码
- 复杂度分析
- 题目2:3275. 第 K 近障碍物查询
- 思路
- 代码
- 复杂度分析
- 题目3:3276. 选择矩阵中单元格的最大得分
- 思路
- 代码
- 复杂度分析
- 题目4:3277. 查询子数组最大异或值
- 思路
- 代码
- 复杂度分析
Leetcode 第 413 场周赛题解
题目1:3274. 检查棋盘方格颜色是否相同
思路
找规律。
根据题目中的图片,如果 coordinate[0] 和 coordinate[1] 的 ASCII 值的奇偶性相同,那么格子是黑格,否则是白格。
进一步地,由于奇数+奇数=偶数,偶数+偶数=偶数,所以如果 (coordinate[0]+coordinate[1]) mod 2 是偶数,那么格子是黑格;否则奇数+偶数=奇数,格子是白格。
如果 (coordinate1[0]+coordinate1[1]) mod 2=(coordinate2[0]+coordinate2[1]) mod 2,那么两个格子颜色相同,否则不同。
代码
/*
* @lc app=leetcode.cn id=3274 lang=cpp
*
* [3274] 检查棋盘方格颜色是否相同
*/
// @lc code=start
class Solution
{
public:
bool checkTwoChessboards(string coordinate1, string coordinate2)
{
return (coordinate1[0] + coordinate1[1]) % 2 == (coordinate2[0] + coordinate2[1]) % 2;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(1)。
空间复杂度:O(1)。
题目2:3275. 第 K 近障碍物查询
思路
维护前 k 小元素,可以用最大堆。
遍历数组 queries,计算点 (x,y) 到原点的曼哈顿距离 d=∣x∣+∣y∣。
把 d 入堆,如果堆大小超过 k,就弹出堆顶(最大的元素)。
当堆的大小等于 k 时,堆顶就是第 k 小的距离。
代码
/*
* @lc app=leetcode.cn id=3275 lang=cpp
*
* [3275] 第 K 近障碍物查询
*/
// @lc code=start
class Solution
{
public:
vector<int> resultsArray(vector<vector<int>> &queries, int k)
{
int n = queries.size();
vector<int> ans(n, -1);
priority_queue<int> pq; // 大根堆
for (int i = 0; i < n; i++)
{
int dis = abs(queries[i][0]) + abs(queries[i][1]);
pq.push(dis);
if (pq.size() > k)
pq.pop();
if (pq.size() == k)
{
// 堆顶就是当前第 k 小的距离
ans[i] = pq.top();
}
}
return ans;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(mlogk),其中 m 是数组 queries 的长度。单次插入的时间复杂度是O(logk)。
空间复杂度:O(k)。返回值不计入。
题目3:3276. 选择矩阵中单元格的最大得分
思路
用回溯会超时:
/*
* @lc app=leetcode.cn id=3276 lang=cpp
*
* [3276] 选择矩阵中单元格的最大得分
*/
// @lc code=start
class Solution
{
private:
int m, n;
int ans = 0;
public:
int maxScore(vector<vector<int>> &grid)
{
m = grid.size(), n = m ? grid[0].size() : 0;
unordered_set<int> s;
backtrace(0, 0, s, grid);
return ans;
}
// 辅函数 - 回溯
void backtrace(int level, int score, unordered_set<int> s, vector<vector<int>> &grid)
{
if (level == m)
{
ans = max(ans, score);
return;
}
for (int j = 0; j < n; j++)
{
if (s.count(grid[level][j]) == 0)
{
s.insert(grid[level][j]);
score += grid[level][j];
backtrace(level + 1, score, s, grid);
score -= grid[level][j];
s.erase(grid[level][j]);
backtrace(level + 1, score, s, grid);
}
}
}
};
// @lc code=end
用状压 DP。
定义状态为 dp[i][j],表示在 [1,i] 中选数字,所选数字所处的行号不能在集合 j 中,每行至多一个数且没有相同元素时,所选元素之和的最大值。
讨论元素 i 选或不选:
- 不选 i,问题变成在 [1,i−1] 中选数字,所选数字所处的行号不能在集合 j 中,每行至多一个数且没有相同元素时,所选元素之和的最大值,即 dp[i-1][j]。
- 选 i,枚举选第 k 行的 i(提前预处理 i 所处的行号),问题变成在 [1,i−1] 中选数字(注意 i 只能选一次),所选数字所处的行号不能在集合 j∪{k} 中,每行至多一个数且没有相同元素时,所选元素之和的最大值,即 dp[i-1][j∪{k}]。
两种情况取最大值。
代码
// 状压 DP
class Solution
{
public:
int maxScore(vector<vector<int>> &grid)
{
int m = grid.size();
unordered_map<int, int> pos;
for (int i = 0; i < m; i++)
{
for (int &x : grid[i])
{
// 保存所有包含 x 的行号(压缩成二进制数)
pos[x] |= 1 << i;
}
}
vector<int> all_nums;
for (auto &[x, _] : pos)
{
all_nums.push_back(x);
}
int u = 1 << m;
vector<vector<int>> dp(all_nums.size() + 1, vector<int>(u));
for (int i = 0; i < all_nums.size(); i++)
{
int x = all_nums[i];
for (int j = 0; j < u; j++)
{
dp[i + 1][j] = dp[i][j]; // 不选 x
for (int t = pos[x], lb; t; t ^= lb)
{
lb = t & -t; // lb = 1<<k,其中 k 是行号
if ((j & lb) == 0) // 没选过第 k 行的数
{
dp[i + 1][j] = max(dp[i + 1][j], dp[i][j | lb] + x); // 选第 k 行的 x
}
}
}
}
return dp.back()[0];
}
};
复杂度分析
时间复杂度:O(m * n * 2m),其中 m 和 n 分别为二维矩阵 grid 的行数和列数。
空间复杂度:O(m * n + 2m),其中 m 和 n 分别为二维矩阵 grid 的行数和列数。
题目4:3277. 查询子数组最大异或值
思路
考虑数组的异或值(最后剩下的元素)是由哪些元素异或得到的。
例如数组为 [a,b,c],那么操作一次后变成 [a⊕b, b⊕c],再操作一次,得到 a⊕b⊕b⊕c。其中 b 异或了 2 次。
为方便描述,下面把 a⊕b 简记为 ab,把 a⊕b⊕b⊕c 简记为 ab2c。
又例如数组为 [a,b,c,d],那么操作一次后变成 [ab,bc,cd],再操作一次,变成 [ab2c,bc2d],再操作一次,得到 ab3c3d。
可以发现,ab3c3d 相当于数组 [a,b,c] 的异或值,再异或 [b,c,d] 的异或值。
第一个区间 DP:
定义 dp[i][j] 表示下标从 i 到 j 的子数组的「数组的异或值」,根据上面的讨论,有 dp[i][j]=dp[i][j−1]⊕dp[i+1][j]。
初始值:dp[i][i]=nums[i]。
第二个区间 DP:
为了回答询问,我们需要计算下标从 i 到 j 的子数组中的所有子数组的 f 值的最大值,将其记作 mx[i][j]。
分类讨论:
- 取 dp[i][j] 作为最大值。
- 最大值对应的子数组,右端点不是 j,那么问题变成下标从 i 到 j−1 的子数组中的所有子数组的 dp 值的最大值,即 mx[i][j−1]。
- 最大值对应的子数组,左端点不是 i,那么问题变成下标从 i+1 到 j 的子数组中的所有子数组的 dp 值的最大值,即 mx[i+1][j]。
三者取最大值,得 mx[i][j]=max(dp[i][j],mx[i][j−1],mx[i+1][j])。
初始值:mx[i][i]=nums[i]。
回答询问时直接查询 mx 数组。
代码
/*
* @lc app=leetcode.cn id=3277 lang=cpp
*
* [3277] 查询子数组最大异或值
*/
// @lc code=start
class Solution
{
public:
vector<int> maximumSubarrayXor(vector<int> &nums, vector<vector<int>> &queries)
{
int n = nums.size();
vector<vector<int>> dp(n, vector<int>(n));
vector<vector<int>> mx(n, vector<int>(n));
// 初始化
for (int i = 0; i < n; i++)
{
dp[i][i] = nums[i];
mx[i][i] = nums[i];
}
// 状态转移
for (int i = n - 1; i >= 0; i--)
for (int j = i + 1; j < n; j++)
{
dp[i][j] = dp[i][j - 1] ^ dp[i + 1][j];
mx[i][j] = max(dp[i][j], max(mx[i + 1][j], mx[i][j - 1]));
}
vector<int> ans;
for (vector<int> &q : queries)
ans.push_back(mx[q[0]][q[1]]);
return ans;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(n2+q),其中 n 是数组 nums 的长度,q 是数组 queries 的长度。
空间复杂度:O(n2),其中 n 是数组 nums 的长度。