力扣第135场双周赛


前言

本文会根据我看过的讲解结合我自己的理解,编写力扣竞赛的题解,不定期更新




第一题:求出硬币游戏的赢家

考察类型:模拟

在这里插入图片描述

思路

  • 对于这种小游戏的简单题,往往都具有规律性,可能与题目中的数据的奇偶性,或者某个数的倍数有关
  • 对于本题,由于题目中的硬币面额所限,能够组成115的硬币只能是1枚价值75和4枚价值10
  • 观察题目可知,由于Alice先执行操作,所以当硬币能取出奇数次1枚价值75和4枚价值10时,Alice就会胜利。

模拟

class Solution {
public:
    string losingPlayer(int x, int y) {
        vector<string> winner = {"Alice", "Bob"};
        int cnt = 0;
        //模拟拿出硬币,直到不能拿出
        while (x >= 1 && y >= 4) {
            x--;
            y -= 4;
            cnt++;
        }
        return winner[(cnt + 1) % 2];
    }
};

优化到O(1)

class Solution {
public:
    string losingPlayer(int x, int y) {
        vector<string> winner = {"Alice", "Bob"};
        int cnt = min(x, y / 4);
        return winner[(cnt + 1) % 2];
    }
};



第二题:操作后字符串的最短长度

考察类型:字符串操作数学

在这里插入图片描述

思路

  • 由题目可知,当字符串中的同一个字符出现3次及3次以上时,可以进行删除操作
  • 当同一个字符出现次数为奇数时,删除后最终会剩余1个字符
  • 当同一个字符出现次数为偶数时,删除后最终会剩余2个字符
  • 可以总结出字符删除的个数为(cnt - 1) / 2 * 2: 字符数量减去1/2得到操作次数, 再* 2即可获得删除的字符个数

代码

class Solution {
public:
    int minimumLength(string s) {
    	//哈希表记录字符个数
        unordered_map<char, int> hash;
        int n = s.size();
        for (char ch: s) {
            hash[ch]++;
        }
        for (auto p: hash) {
           n -= (p.second - 1) / 2 * 2;
        }
        return n;
    }
};



第三题:使差值相等的最少数组改动次数

考察类型:枚举差分

在这里插入图片描述

思路

枚举

  • nums[i]nums[n - i - 1]为对称位置的元素
  • 根据题中的数据范围0 <= nums[i] <= k <= 105可知,nums[i]nums[n - i - 1]的差X的绝对值一定在 0 - k 之间
  • 所以我们可以枚举X的值,算出对应的修改次数,找到最小的修改次数

具体过程

  • 首先我们思考一下,当差值为X时,如何算出修改次数呢?
    • 为方便描述,设两个元素中小的为p,大的为q
    • 可以想到三种情况
    • 第一种是 q - p = X此时不需要操作
    • 第二种是:q - p > X 此时需要修改一个数字
      • 为什么不是修改两个?因为我们只需要缩小两个元素的差距,只需要增大小的值或者减小大的值即可
    • 第三种是:q - p < X此时根据改一个数字能否达成目的,还需要分两种情况
      • 我们思考通过将元素改为0 - 1能够把差值改变的范围是多少?一种是将q(大的元素)改得更大为k,此时差值为k - p,另一种是将p(小的元素)改为0,此时差值为q - 0,取最大max(k - p, q)值即为通过修改所能获得的最大差值,那么就能分两种情况
      • 第一种情况,max(k - p, q) >= X,即可以通过改一个数字将差值修改为X
      • 第二种情况,max(k - p, q) > X,即无法通过改一个数字,只能改两个。
  • 然后我们可以得出
    • ans = min( n / 2 - cnt1[X] + cnt2[0] + cnt2[1] + ... + cnt2[X - 1])
    • 先别懵,听我解释
      • n为数组长度,除以2就是对称数对的个数
      • cnt1数组用于记录q - p等于X的个数,数组下标为对应X
      • cnt2数组用于记录max(q, k - p)等于X的个数,数组下标对应X
      • 所以这个式子的含义:总的数对个数 - 本来就等于X的数对的个数 + 小于X的所有max(k - p, q)的个数,遍历每个X得到的结果取最小值
      • 注:总的数对个数 - 本来就等于X的数对的个数默认了剩下的都是操作一步,所以加上需要两步操作的个数,才是最终结果

代码

	class Solution {
public:
    //X = 0,1,2...K
    //ans = n / 2 - (q - p) + sum(max(k - p, q))
    int minChanges(vector<int>& nums, int k) {
        //枚举
        int n = nums.size();
        vector<int> cnt1(k + 1, 0);
        vector<int> cnt2(k + 1, 0);

        //统计差值等于X的对数和max(k - q,q)的对数
        for (int i = 0; i < n / 2; i++) {
            int p = min(nums[i], nums[n - i - 1]);
            int q = max(nums[i], nums[n - i - 1]);
            cnt1[q - p]++;
            cnt2[max(k - p, q)]++;
        }

        //枚举X = 0,1...k
        int ans = INT_MAX;
        int sum2 = 0;
        for (int i = 0; i <= k; i++) {
            ans = min(ans, n / 2 - cnt1[i] + sum2);
            sum2 += cnt2[i];
        }
        
        return ans;
    }
};

差分

  • 根据上文的分析,我们换个角度思考,枚举的方法是根据每个X的可能取值算出对应的修改次数,然后取最小次数,那么我们可不可以遍历数组中所有数对的差值,分析这些差值在修改为每个X的可能取值的贡献呢?
  • 答案是肯定的,要不然我也不会写这个方法,那么如果一个数对的差值为curX,那么他对能够组成X所有取值的贡献是多少呢?由前文分析可知,一个数对的差值为curX,那么吧curX变为0 到 curX只需要修改一个数字,变为curX 到 max(k - p, q) 也只需要修改一个数字,变为max(k - p, q) 到 k则需要修改2个数字
  • 根据这个特点,我们可以将差值变为X所需修改次数映射到数组,即d[X]表示将所有对称数对差值修改为X所需次数
  • 则可写出如下代码
class Solution {
public:
    //遍历数组中每对数对的差值X,计算出该数对对修改次数的贡献
    int minChanges(vector<int>& nums, int k) {
        int n = nums.size();
        //差分
        vector<int> d(k + 2, 0);

        for (int i = 0; i < n / 2; i++) {
            int p = min(nums[i], nums[n - i - 1]);
            int q = max(nums[i], nums[n - i - 1]);
            int X = q - p;
            int mx = max(k - p, q);
            //该数对对差值修改为 0 到 X-1贡献为1
            d[0] += 1;
            d[X] -= 1;
            //该数对对差值修改为 X+1 到 mx贡献为1
            d[X + 1] += 1;
            d[mx + 1] -= 1;
            //该数对对差值修改为 mx + 1 到 k贡献为2
            d[mx + 1] += 2;
        }
        
        int result = d[0];
        for (int i = 1; i < k + 2; i++) {
            d[i] += d[i - 1];
            result = min(result, d[i]);
        }

        return result;
    }
};



第四题:网格图操作后的最大分数(本蒟蒻还不会😭,以后更新)

在这里插入图片描述
本蒟蒻先放弃了

总结

本次双周赛考察了模拟数学枚举差分
虽然竞赛时本蒟蒻只做出来了2道题,还是希望能给大家带来一点帮助

  • 27
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值