力扣第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,即无法通过改一个数字,只能改两个。
- 我们思考通过将元素改为0 - 1能够把差值改变的范围是多少?一种是将
- 然后我们可以得出
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道题,还是希望能给大家带来一点帮助