题目列表
一、使两个整数相等的位更改次数
这题就是看n的二进制位上的1是否包含k的二进制位上的1,即(n&k)==k是否为真,如果为真,则能实现,返回多余的1的个数即可,如果为假,返回-1,代码如下
class Solution {
public:
int minChanges(int n, int k) {
return (n&k)==k ? __builtin_popcount(n-k) : -1;
}
};
二、字符串元音游戏
这题是个脑经急转弯,我们先统计给定字符串中的元音字母出现的次数,然后如果没有思路,我们可以先举几个例子来找找规律。
设 cnt 表示 元音字母出现的次数
- 当 cnt = 0 时,小红无法操作,小明赢
- 当 cnt = 1 时,小红移除1个,小明无法操作,小红赢
- 当 cnt = 2 时,小红移除1个,小明无法操作,小红赢
- 当 cnt = 3 时,小红移除3个,小明无法操作,小红赢
- 当 cnt = 4 时,小红移除3个,小明无法操作,小红赢
- ......
我们不难发现规律,只有当cnt=0时小明才能赢,否则都是小红赢,因为当cnt>=1时,如果cnt为奇数,小红直接全部移除,小明无法操作,如果cnt为偶数,小红移除cnt - 1个,剩余 1 个,小明依旧无法操作,所以小红赢
代码如下
class Solution {
const string t = "aeiou";
public:
bool doesAliceWin(string s) {
for(auto e:s)
if(t.find(e)!=string::npos)
return true;
return false;
}
};
三、将1移动到末尾的最大操作次数
这题用贪心,显然我们从前往后依次移动1,这样得到的操作次数最大,也就是让1移动的距离尽可能的短,从而让1移动的次数更多,已知每个1会一直向右移动到头或者移动到下一个1,所以优先移动左边的1,让右边的1不动,会得到最优解。代码如下
class Solution {
public:
int maxOperations(string s) {
int n = s.size();
int i = 0, cnt = 0, ans = 0;
while(i < n){
while(i < n && s[i] == '0') i++;
ans += cnt;
while(i < n && s[i] == '1') cnt++, i++;
}
return ans;
}
};
四、使数组等于目标数组所需的最小操作次数
这题主要考察对差分数组的理解,看到题目中有对子数组进行+1/-1操作,就要想到差分数组,它能帮助我们在O(1)的事件内完成对数组中一段连续区间的加减操作。
那么这题如何用差分数组来做呢?
首先我们要先算出每个元素和目标元素的差值,即 nums 和 target 的差值,记为 数组 a,然后通过对区间的+1/-1操作,让数组 a 的元素全为 0,并且操作次数要求最少。
这里先说明一点:数组 a 的元素全为 0 <=> 数组 a 的差分数组的元素全为 0,因为数组 a 和数组 a 的差分数组是可以相互推导出来的。所以 问题就从 通过区间操作让数组 a 的元素变为 0 转换成了 通过+1/-1操作让数组 a 的差分数组的元素变为 0,记差分数组为 diff
显然,问题被简化了,这也就是我们使用差分数组来解题的原因
那么怎样才能让操作次数最少呢?举个例子大家就明白了
为啥上面做法得到的操作数最少呢?因为我们尽可能多的使用了免费的操作次数,本质是一种贪心
代码如下
class Solution {
public:
long long minimumOperations(vector<int>& nums, vector<int>& target) {
int n = nums.size();
long long diff[n];
diff[0] = nums[0] - target[0];
for(int i = 1; i < n; i++)
diff[i] = (nums[i] - target[i]) - (nums[i-1] - target[i-1]);
long long ans = 0, cnt = 0; // cnt 记录可以免费进行的操作
for(auto x:diff){
if(x == 0) continue;
int tmp = cnt + x;
if(x > 0 && cnt < 0){
x -= min(x, -cnt);
}else if(x < 0 && cnt > 0){
x += min(-x, cnt);
}
ans += abs(x);
cnt = tmp;
}
return ans;
}
};