周赛 337 题解

plus2047 于 2023-03-19 发布

最近一段时间非常繁忙,周赛又已经鸽了好几周了。其实维护这个公众号本意还是以后发布更多有意思的东西,希望今年不要拖延症了。

本周第三题貌似比第四题还难。

1.

image

无甚可说,数数问题。这里使用位运算实现,可以用 Python 的 bin 函数转成字符串进行统计。

注意这个问题中是从右往左数比特位的,题目条件没有说的很清楚。

class Solution {
public:
    vector<int> evenOddBit(int n) {
        int even = 0, odd = 0, i = 0, mask = 1;
        while(mask <= n) {
            if(mask & n) {
                if(i % 2) {
                    odd++;
                } else {
                    even++;
                }
            }
            i++;
            mask <<= 1;
        }
        return {even, odd};
    }
};

2.

image image

基本思路是,从 0 号格子开始,按顺序逐个检查。每个格子跟上一个格子比较一下坐标差,从而判断是否是 Knight 可以到达的位置。

这个代码写的比较长是因为做了很多额外的检查。

class Solution {
public:
    bool checkValidGrid(vector<vector<int>>& grid) {
        int n = grid.size();
        vector<tuple<int, int, int>> p(n * n);  // <grid_num, i_index, j_index>
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++) {
                p[i * n + j] = {grid[i][j], i, j};
            }
        }
        sort(p.begin(), p.end());
        for(int k = 0; k < n * n; k++) {
            int x, i, j;
            tie(x, i, j) = p[k];
            if(k == 0) {
                if(x != 0 or i != 0 or j != 0) return false;
            } else {
                int _x, _i, _j;
                tie(_x, _i, _j) = p[k - 1];
                if(_x + 1 != x) return false;
                int di = abs(_i - i), dj = abs(_j - j);
                if(not(di == 1 and dj == 2 or di == 2 and dj == 1)) return false;
            }
        }
        return true;
    }
};

3.

image

这个问题直觉上是可以有多项式速度的解法的,但既然问题规模只有 20, 可以使用 bit mask 枚举所有子集,然后一一检查。但是这份代码过的比较悬,做了一些细节优化才不会 TLE.

class Solution {
public:
    int beautifulSubsets(vector<int>& nums, int k) {
        const int v = *max_element(nums.begin(), nums.end()) + 1;
        const int n = nums.size();
        sort(nums.begin(), nums.end());
        
        vector<char> seen(v);
        int res = 0;
        unsigned mm = 1 << n;
        
        for(unsigned mask = 1; mask < mm; mask++) {
            bool succ = true;
            int i = 0;
            for(; i < n and succ; i++) {
                if(mask & (1 << i)) {
                    int x = nums[i];
                    seen[x] = true;
                    if(x - k > 0 and seen[x - k] or x + k < v and seen[x + k]) {
                        succ = false;
                    }
                }
            }
            for(; i >= 0; i--) {
                if(mask & (1 << i)) {
                    seen[nums[i]] = false;
                }
            }
            res += succ;
        }
        
        return res;
    }
};

4.

image

这个题目我反而感觉比第三题要简单。题述操作实际上可以归结为一个数 x 可以替换为任意一个关于 value 同余的数 y 也即只要满足 x % value = y % value 即可。很容易想到,无论其他数字是什么,关于 value 同余的一组数最佳变换方案就是变换成从 x % value 开始间隔 value 的数列。

比如,value = 5 时,则所有 x % value = 1 的数字最佳变换方案就是 1, 6, 11, ....

class Solution {
public:
    int findSmallestInteger(vector<int>& nums, int value) {
        // 求 x % value, 注意处理负数
        const int n = nums.size();
        for(int i = 0; i < n; i++) {
            nums[i] = ((nums[i] % value) + value) % value;
        }
        // 排序之后同余的数都是相邻的
        sort(nums.begin(), nums.end());

        // 执行变换
        for(int i = 0; i < n; i++) {
            if(i != 0 and nums[i] % value == nums[i - 1] % value) {
                nums[i] = nums[i - 1] + value;
            }
        }

        // 排序之后寻找第一个缺失的非负整数
        sort(nums.begin(), nums.end());
        if(nums[0] != 0) return 0;
        for(int i = 1; i < n; i++) {
            if(nums[i] != nums[i - 1] + 1) {
                return nums[i - 1] + 1;
            }
        }
        return n;
    }
};