文章目录
- 周赛337
- [6319. 奇偶位数](https://leetcode.cn/problems/number-of-even-and-odd-bits/)
- [6322. 检查骑士巡视方案](https://leetcode.cn/problems/check-knight-tour-configuration/)
- [6352. 美丽子集的数目](https://leetcode.cn/problems/the-number-of-beautiful-subsets/)
- [6321. 执行操作后的最大 MEX](https://leetcode.cn/problems/smallest-missing-non-negative-integer-after-operations/)
周赛337
6319. 奇偶位数
难度简单0
给你一个 正 整数 n
。
用 even
表示在 n
的二进制形式(下标从 0 开始)中值为 1
的偶数下标的个数。
用 odd
表示在 n
的二进制形式(下标从 0 开始)中值为 1
的奇数下标的个数。
返回整数数组 answer
,其中 answer = [even, odd]
。
示例 1:
输入:n = 17
输出:[2,0]
解释:17 的二进制形式是 10001 。
下标 0 和 下标 4 对应的值为 1 。
共有 2 个偶数下标,0 个奇数下标。
示例 2:
输入:n = 2
输出:[0,1]
解释:2 的二进制形式是 10 。
下标 1 对应的值为 1 。
共有 0 个偶数下标,1 个奇数下标。
提示:
1 <= n <= 1000
模拟
class Solution {
public int[] evenOddBit(int n) {
int time = 0;
int[] res = new int[2];
while(n > 0){
int i = n % 2;
time++;
if(i == 1) res[1 - time % 2]++;
n /= 2;
}
return res;
}
}
优雅写法:
① n&1
,取最低比特位
② n >>= 1
class Solution {
public int[] evenOddBit(int n) {
// i ^= 1 代替变量%2
var ans = new int[2];
for (int i = 0; n > 0; i ^= 1, n >>= 1)
ans[i] += n & 1;
return ans;
}
}
6322. 检查骑士巡视方案
难度中等0
骑士在一张 n x n
的棋盘上巡视。在有效的巡视方案中,骑士会从棋盘的 左上角 出发,并且访问棋盘上的每个格子 恰好一次 。
给你一个 n x n
的整数矩阵 grid
,由范围 [0, n * n - 1]
内的不同整数组成,其中 grid[row][col]
表示单元格 (row, col)
是骑士访问的第 grid[row][col]
个单元格。骑士的行动是从下标 0 开始的。
如果 grid
表示了骑士的有效巡视方案,返回 true
;否则返回 false
。
注意,骑士行动时可以垂直移动两个格子且水平移动一个格子,或水平移动两个格子且垂直移动一个格子。下图展示了骑士从某个格子出发可能的八种行动路线。
示例 1:
输入:grid = [[0,11,16,5,20],[17,4,19,10,15],[12,1,8,21,6],[3,18,23,14,9],[24,13,2,7,22]]
输出:true
解释:grid 如上图所示,可以证明这是一个有效的巡视方案。
示例 2:
输入:grid = [[0,3,6],[5,8,1],[2,7,4]]
输出:false
解释:grid 如上图所示,考虑到骑士第 7 次行动后的位置,第 8 次行动是无效的。
提示:
n == grid.length == grid[i].length
3 <= n <= 7
0 <= grid[row][col] < n * n
grid
中的所有整数 互不相同
排列型回溯(BFS)
class Solution {
int[][] dict = new int[][]{{2, 1}, {-2, 1}, {2, -1}, {-2, -1}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}};
public boolean checkValidGrid(int[][] grid) {
if(grid[0][0] != 0) return false;
int n = grid.length, m = grid[0].length;
Deque<int[]> dq = new ArrayDeque<>();
dq.addLast(new int[]{0, 0, 0});
int cnt = 0; // 访问的格子数
while(!dq.isEmpty()){
int[] x = dq.poll();
cnt++;
for(int[] d : dict){
int nx = x[0] + d[0], ny = x[1] + d[1];
if(nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] == x[2]+1)
dq.addLast(new int[]{nx, ny, grid[nx][ny]});
}
}
return cnt == n*m;
}
}
6352. 美丽子集的数目
难度中等5
给你一个由正整数组成的数组 nums
和一个 正 整数 k
。
如果 nums
的子集中,任意两个整数的绝对差均不等于 k
,则认为该子数组是一个 美丽 子集。
返回数组 nums
中 非空 且 美丽 的子集数目。
nums
的子集定义为:可以经由 nums
删除某些元素(也可能不删除)得到的一个数组。只有在删除元素时选择的索引不同的情况下,两个子集才会被视作是不同的子集。
示例 1:
输入:nums = [2,4,6], k = 2
输出:4
解释:数组 nums 中的美丽子集有:[2], [4], [6], [2, 6] 。
可以证明数组 [2,4,6] 中只存在 4 个美丽子集。
示例 2:
输入:nums = [1], k = 1
输出:1
解释:数组 nums 中的美丽数组有:[1] 。
可以证明数组 [1] 中只存在 1 个美丽子集。
提示:
1 <= nums.length <= 20
1 <= nums[i], k <= 1000
子集型回溯变体
答案的角度:枚举i位置选哪个
class Solution {
int[] nums;
int k;
Set<Integer> set = new HashSet<>();
int res = -1; // 排除空集
public int beautifulSubsets(int[] nums, int k) {
// 不要求连续 枚举所有子集,在枚举过程中加个判断
// 在选择 x = nums[i] 的时候 若之前选过x-k 或者 x+k,就不选了
this.nums = nums; this.k = k;
dfs(0);
return res;
}
public void dfs(int idx){
res++;
if(idx == nums.length){
return;
}
for(int j = idx; j < nums.length; j++){
int x = nums[j];
if(set.contains(x-k) || set.contains(x+k)) continue;
set.add(x);
dfs(j+1);
set.remove(x);
}
}
}
输入的角度:选i还是不选
class Solution {
int[] nums;
int k;
Set<Integer> set = new HashSet<>();
int res = -1; // 排除空集
public int beautifulSubsets(int[] nums, int k) {
// 不要求连续 枚举所有子集,在枚举过程中加个判断
// 在选择 x = nums[i] 的时候 若之前选过x-k 或者 x+k,就不选了
this.nums = nums; this.k = k;
dfs(0);
return res;
}
public void dfs(int idx){
if(idx == nums.length){
res++;
return;
}
// 不选
dfs(idx + 1);
// 选,要先判断一下
int x = nums[idx];
if(!set.contains(x-k) && !set.contains(x+k)){
set.add(x);
dfs(idx+1);
set.remove(x);
}
}
}
动态规划【✨】
6321. 执行操作后的最大 MEX
难度中等2
给你一个下标从 0 开始的整数数组 nums
和一个整数 value
。
在一步操作中,你可以对 nums
中的任一元素加上或减去 value
。
- 例如,如果
nums = [1,2,3]
且value = 2
,你可以选择nums[0]
减去value
,得到nums = [-1,2,3]
。
数组的 MEX (minimum excluded) 是指其中数组中缺失的最小非负整数。
- 例如,
[-1,2,3]
的 MEX 是0
,而[1,0,3]
的 MEX 是2
。
返回在执行上述操作 任意次 后,nums
的最大 MEX 。
示例 1:
输入:nums = [1,-10,7,13,6,8], value = 5
输出:4
解释:执行下述操作可以得到这一结果:
- nums[1] 加上 value 两次,nums = [1,0,7,13,6,8]
- nums[2] 减去 value 一次,nums = [1,0,2,13,6,8]
- nums[3] 减去 value 两次,nums = [1,0,2,3,6,8]
nums 的 MEX 是 4 。可以证明 4 是可以取到的最大 MEX 。
示例 2:
输入:nums = [1,-10,7,13,6,8], value = 7
输出:2
解释:执行下述操作可以得到这一结果:
- nums[2] 减去 value 一次,nums = [1,-10,0,13,6,8]
nums 的 MEX 是 2 。可以证明 2 是可以取到的最大 MEX 。
提示:
1 <= nums.length, value <= 105
-109 <= nums[i] <= 109
同余分组
题解:https://leetcode.cn/problems/smallest-missing-non-negative-integer-after-operations/solution/tong-yu-pythonjavacgo-by-endlesscheng-qoan/
同余:如果(x-y) mod m = 0
,那么称x
与y
对模m
同余
class Solution {
public int findSmallestInteger(int[] nums, int m) {
// 对于最终的数组,能否操作出一个0?
// 对于最终的数组,能否操作出一个1?
// ...
// 取余
// 统计余数出现的次数,从0到最大MEX依次枚举,制造不出res就是答案
var cnt = new HashMap<Integer, Integer>();
for(int x : nums){
cnt.merge((x % m + m) % m, 1, Integer::sum); // cnt[(x%m+m)%m]++
// 等同于 cnt.put((x%m+m)%m, cnt.getOrDefault((x%m+m)%m,0)+1);
}
int mex = 0;
while (cnt.merge(mex % m, -1, Integer::sum) >= 0) // cnt[mex%m]-1 >= 0
++mex;
// 等同于:
// while(cnt.getOrDefault(mex%m, 0) > 0){
// cnt.put((mex%m+m)%m, cnt.getOrDefault((mex%m+m)%m,0)-1);
// ++mex;
// }
return mex;
}
}
}
int mex = 0;
while (cnt.merge(mex % m, -1, Integer::sum) >= 0) // cnt[mex%m]-1 >= 0
++mex;
// 等同于:
// while(cnt.getOrDefault(mex%m, 0) > 0){
// cnt.put((mex%m+m)%m, cnt.getOrDefault((mex%m+m)%m,0)-1);
// ++mex;
// }
return mex;
}
}