【上分日记】382场周赛(填补法 + 位运算 + 奇偶性 + 枚举)

文章目录

  • 前言
  • 正文
    • 1.3020. 子集中元素的最大数量
    • 2.3021. Alice 和 Bob 玩鲜花游戏
    • 3.3022. 给定操作次数内使剩余元素的或值最小
  • 总结
  • 尾序

前言

  hello!各位C友们好呀,博主又来写题解了,这场周赛,博主只写了前三题(hhh, 第四题是真不会),这一场周赛说难也难,说简单也简单,难在第4题太难想了,简单在前三题知道相应的知识还是比较好想的,那么废话不多说,直接进入今天的正题吧!

正文

1.3020. 子集中元素的最大数量

  • 题目链接:子集中元素的最大数量

  • 这道题,不知道各位wa了几次呢?我是wa了3次,喜提15分钟罚时。
  • 题目思路:
  1. 哈希表记录元素出现次数。
  2. 初始化ans为1的出现次数的最大奇数次(坑1,因为1的不管多少次方都是1,避免陷入死循环)。
  3. 遍历数组,从小到大暴力枚举最长的字集中的最大数量。
  1. 说明:这里的最大数量必然为奇数次,如果枚举的数的出现次数大于等于2时,就有接着枚举的可能,可以接着枚举,如果枚举的数的出现次数等于1,则无法接着枚举,因此最后的数量为枚举的数的种类 * 2 + 1。
  2. 坑2 : 如果枚举是用平方的话,可能会爆int,因此需要判断一下。
  3. 细节: 这里从小到大每次都乘本身,其实枚举的次数是很小的。
  • 实现代码:
class Solution {
public:
    int maximumLength(vector<int>& nums) 
    {
        unordered_map<int,int> hash;
        for(auto e : nums)
            hash[e]++;
        
        //坑1:初始值为1的最大奇数次长度。
        int ans = hash[1] - (hash[1] % 2 ^ 1);
        int MAX = 1e9;
        for(auto e : nums)
        {
            if(e == 1) continue; //避免陷入死循环
            long long val = e;
            int res = 0,len = 0;
            //坑2: val 可能会爆int
            while(val <= MAX && hash.count(val))
            {
                res = 2 * len + 1;
                if(hash[val] == 1)
                    break;
                len++;
                val *= val;
            }
            ans = max(res,ans);
        }
        return ans;
    }
};

2.3021. Alice 和 Bob 玩鲜花游戏

  • 题目链接:Alice 和 Bob 玩鲜花游戏

  • 题目思路:
  • 这是一道阅读理解题,把题目意思读明白了,这道题其实很简单。
  • 关键点:Alice先走,每个人必须每次摘一朵,先摘完的赢。—— 只要花的总数是奇数即可。

小小数学知识点:

  1. 奇 + 奇 = 偶, 奇 - 奇 = 偶, 偶 + 偶 = 偶, 偶 - 偶 = 偶。
  2. 奇 + 偶 = 奇,奇 - 偶 = 奇。
  3. 有[1,n] 个数,有 (n + 1) / 2个奇数,n / 2 个偶数。
  • 因此:我们只需要枚举(x,y), x能取多少个奇数,y能取多少个偶数,相乘得情况1,x能取多少个偶数,y能取多少个奇数,相乘得情况2,两种情况之和即为结果。

  • 实现代码:
class Solution {
public:
    long long flowerGame(int n, int m) 
    {
        long long x = n,y = m;
        long long ans1 = ((x + 1) / 2) * (y / 2);
        long long ans2 = (x / 2) *((y + 1) / 2);  
        return ans1 + ans2;
    }
};
  • 细节:注意用 () 表明计算的优先顺序,因为计算机有上下取整的问题,不带括号算起来是有误差的。

  • 小小思考技巧:
    【上分日记】382场周赛(填补法 + 位运算 + 奇偶性 + 枚举)_第1张图片

  • 实现代码:
class Solution {
public:
    long long flowerGame(int n, int m) 
    {
        return (long long)n * m / 2;
    }
};

3.3022. 给定操作次数内使剩余元素的或值最小

  • 题目链接:给定操作次数内使剩余元素的或值最小

  • 题目思路:
  • 此题难在难想出思路,想出来倒还是比较简单的,代码也比较好写。好写归好写,博主在此题坐牢了将近一个小时,也憋不出半句代码,hhh。
  1. 从二进制的角度思考,或运算,只要有1就会保留,但是我们可以通过与操作,通过相邻的0通过与1按位与变为0。
  2. 且要使或值最小,我们要从高位的1,进行逐步验证看是否能在k次操作内变为0。这里就需要分析得出一个重要结论:
  1. 如果某一段数字能变为0,那么就为那一段长度减1。
  2. 如果某一段数字不能变为0,那么就为那那一段的长度。
  3. 特殊的:如果那一段长度全为1,则需要nums.length,但 k < nums.length,因此无需考虑这一情况。
  1. 高位按位与的顺序会对低位产生影响,因此我们应保留高位能枚举的1位,继续往下枚举看是否可能为在k次操作内变为0。也就是在高位至多k次按位与变为0的前提下,看低位是否也能在至多k次按位与变为0。
  1. 如果不能变为0,则答案中必然有这一位,将这一位按位或到答案上,同时删除这一位不能枚举的高位。
  2. 如果能变为0,则答案中这一位为0。保留这一位继续往下枚举即可。
class Solution {
public:
    int minOrAfterOperations(vector<int>& nums, int k) 
    {
        int ans = 0,mask = 0;
        for(int i = 29; i >= 0; i--)
        {
            mask |= 1 << i;
            int res = -1; //补码全为1,用于初始化第一个数
            int cnt = 0;
            for(auto num : nums)
            {
                res &= num & mask;
                if(res) cnt++;
                else
                {
               	 /*
                  变为0之后,无需再管,避免后面&的结果一直为0,需要再进行初始化
                  之后看下一段1变为0的操作次数。
                  */
                    res = -1;
                }
            }
            if(cnt > k)
            {
                ans |= (1 << i); //此位的1必定在答案中出现。
                mask ^= (1 << i); 
                //删除不能变为0的一位,以防止对后面的操作产生影响。
            }
        }
        return ans;
    }
};

总结

 本场周赛考察了哈希枚举,奇偶性,位运算(从高位枚举填写答案)。希望本篇文章对大家有所帮助吧!

尾序

我是舜华,期待与你的下一次相遇!

你可能感兴趣的:(上分日记,位运算,奇偶性,填补法,哈希,枚举)