LeetCode第241场周赛

Posted machine_gun_lin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode第241场周赛相关的知识,希望对你有一定的参考价值。

第一题 5759. 找出所有子集的异或总和再求和

题目链接:5759. 找出所有子集的异或总和再求和

  • 直接爆搜,计算所有可能的子集的异或和curSum,加入到答案ans
class Solution {
private:
    int ans;
    int n;
    
    
public:
    void dfs(vector<int>& nums, int u, int curSum) {
        if(u == n) {
            return ;
        }
        curSum ^= nums[u];
        ans += curSum;
        dfs(nums, u + 1, curSum);
        curSum ^= nums[u];
        dfs(nums, u + 1, curSum);
    }
    
    int subsetXORSum(vector<int>& nums) {
        n = nums.size();
        dfs(nums, 0, 0);
        return ans;
    }
};

第二题 5760. 构成交替字符串需要的最小交换次数

题目链接:5760. 构成交替字符串需要的最小交换次数

  • 先扫描一遍字符串,计算10的个数cntOnecntZero
  • 如果能组成合法的交替字符串,那么cntOnecntZero的差的绝对值小于1
  • 贪心,枚举以1或以0开头的情况,计算最小的交换次数
  • 1的个数比0的个数多1时,开头的元素只能是1,反之同理
  • 如果10的个数一样多,那就分别枚举10开头的情况,取较小的那个交换次数作为答案
class Solution {
private:
    int cntOne;
    int cntZero;
    int n;
    int ans;
    
public:
    int minSwaps(string s) {
        n = s.size();
        for(int i = 0; i < n; ++i) {
            if(s[i] == \'1\') {
                ++cntOne;
            } else {
                ++cntZero;
            }
        }
        if(abs(cntOne - cntZero) > 1) {       // 无法组成交替字符串
            return -1;
        }
        if(cntOne > cntZero) {        // 1 的个数比 0 多,则只能以 1 开头
             for(int i = 0; i < n; ++i) {
                if(i & 1) {
                    if(s[i] != \'0\') {
                        ++ans;
                    }
                } else {
                    if(s[i] != \'1\') {
                        ++ans;
                    }
                }
            }
            return ans / 2;
        } else if(cntOne < cntZero) {    // 0 的个数比 1 多,则只能以 0 开头
            for(int i = 0; i < n; ++i) {
                if(i & 1) {
                    if(s[i] != \'1\') {
                        ++ans;
                    }
                } else {
                    if(s[i] != \'0\') {
                        ++ans;
                    }
                }
            }
            return ans / 2;
        } else {             // 0 和 1 的个数一样多,以 1 或 0 开头都行
            int ans0 = 0, ans1 = 0;
            for(int i = 0; i < n; ++i) {    // 以 0 开头
                if(i & 1) {
                    if(s[i] != \'1\') {
                        ++ans0;
                    }
                } else {
                    if(s[i] != \'0\') {
                        ++ans0;
                    }
                }
            }
            for(int i = 0; i < n; ++i) {        // 以 1 开头
                if(i & 1) {
                    if(s[i] != \'0\') {
                        ++ans1;
                    }
                } else {
                    if(s[i] != \'1\') {
                        ++ans1;
                    }
                }
            }
            return min(ans0 / 2, ans1 / 2);
        }
        return 0;
    }
};

第三题 5761. 找出和为指定值的下标对

题目链接:5761. 找出和为指定值的下标对

  • 很容易想到用两个哈希表分别记录数组中所有元素的个数,不过这样会超时
  • 优化:考虑到nums1的规模小于1000,遍历哈希表的操作又比较慢,因此nums1不用哈希表,直接遍历
  • FindSumPairs时直接初始化nums2对应的哈希表即可
  • add操作也是直接对nums2的哈希表进行修改
  • count操作时,遍历nums1,到nums2对应的哈希表寻找另一个数的个数,加入到答案ans
#pragma GCC optimize(3,"Ofast","inline")

class FindSumPairs {
private:
    map<int, int> hash2;         // nums2 数组对应的哈希表,记录所有元素以及它们在 nums2 中的个数
    vector<int> nums1, nums2;
    int n, m;        // 两个数组的大小
    int ans;         // count 的答案
    
public:
    FindSumPairs(vector<int>& _nums1, vector<int>& _nums2) {
        nums1 = _nums1, nums2 = _nums2;
        n = nums1.size();
        m = nums2.size();
        for(const auto& num : nums2) {      // 初始化哈希表
            ++hash2[num];
        }
    }
    
    void add(int index, int val) {          // nums2 中有元素发生变化,修改哈希表
        --hash2[nums2[index]];
        if(hash2[nums2[index]] == 0) {
            hash2.erase(nums2[index]);
        }
        nums2[index] += val;
        ++hash2[nums2[index]];
    }
    
    int count(int tot) {
        ans = 0;     
        for(int i = 0; i < n; ++i) {       // 遍历 nums1, 到哈希表中寻找另一个数在 nums2 中出现的次数,加入到答案中
            if(nums1[i] >= tot) {
                continue;
            } else {
                ans += hash2[tot - nums1[i]];
            }
        }
        return ans;
    }
};

/**
 * Your FindSumPairs object will be instantiated and called as such:
 * FindSumPairs* obj = new FindSumPairs(nums1, nums2);
 * obj->add(index,val);
 * int param_2 = obj->count(tot);
 */

第四题 5762. 恰有K根木棍可以看到的排列数目

题目链接:5762. 恰有 K 根木棍可以看到的排列数目

  • 动态规划
  • 从长到短考虑所有木棍的分配位置
  • 状态表示:dp[i][j]:已经分配了前 i 长的木棍(i1开始)、并且从左侧看可以看到j根木棍的方案数
  • 状态转移:已经分配了第1 ~ i长的木棍,现在考虑第i + 1长的木棍
    1. 如果把这根木棍放在当前所有木棍的最左侧,那么从左往右看能看到的木棍数多了一个,因此我们有:dp[i + 1][j + 1] = (dp[i + 1][j + 1] + dp[i][j]) ; 表示把第i + 1长的木棍放在最左侧的方案数
    2. 如果把这根木棍放在前1 ~ i的木棍中任意一个木棍的右侧,从左往右看能看到的木棍数量还是j个,那么由于有i个可以放木棍的位置,因此我们得到方案数:dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]; 表示不把第i + 1长的木棍放在最左侧(而是放在比它长的某根木棍的右侧)的方案数
  • 最终的答案就是,枚举完前 n 根木棍,并且从左往右看能看到 k 根木棍的方案数:dp[n][k]
  • 参考资料:坑神的B站讲解
const int mod = 1e9 + 7;
const int maxn = 1005;
using LL = long long;
LL dp[maxn][maxn];

class Solution {
public:
    int rearrangeSticks(int n, int k) {
        memset(dp, 0, sizeof dp);
        dp[1][1] = 1;
        for(int i = 2; i <= n; ++i) {
            for(int j = 1; j <= k; ++j) {
                dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % mod;
                dp[i][j] = (dp[i][j] + dp[i - 1][j] * (i - 1)) % mod;    
            }
        }
        return dp[n][k];
    }
};

以上是关于LeetCode第241场周赛的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 第 149 场周赛

Leetcode 第175场周赛 题解(完结)

LeetCode 第 155 场周赛

Leetcode第 279 场周赛

Leetcode第187场周赛——菜鸡依旧

LeetCode第 303 场周赛