32分钟3题
直接模拟
class Solution {
public:
int minChanges(int n, int k) {
int ans=0;
for(int i=0;i<20;i++){
if( ((n>>i)&1)==1 && ((k>>i)&1)==0 ){
ans++;
}else if(((n>>i)&1)==0 && ((k>>i)&1) == 1 ){
return -1;
}
}
return ans;
}
};
还可以按集合的思路去做,二进制串"10010",位置表示集合里的元素,相应位置是0表示集合里没有这个元素,是1表示有这个元素。上面的二进制串就表示了{1,4}。n&k 表示交集,n|k 表示并集。这道题只能1变0,如果n能变成k,那么k就是n的子集,等价于 n&k == k。具体的操作数就是n ^ k中1的个数。
class Solution {
public:
int minChanges(int n, int k) {
return (n&k)==k ? __builtin_popcount(n^k) : -1;
}
};
设字符串中元音的个数为n
n为奇数,小红拿走整个串,小红胜
n为偶数,小红拿走奇数个,小明拿走偶数个,还剩奇数个,与上一种情况一样,小红胜
n为0,小红无法操作,小明胜
class Solution {
public:
bool doesAliceWin(string s) {
for(auto& i:s){
if(i=='a' || i=='e' || i=='i' || i=='o' || i=='u')
return true;
}
return false;
}
};
最小操作数是从右往左遍历,遇到1就向右移动
最大操作数是从左向右遍历,遇到0就向右一直移动直到遇到1不能移动
动态规划
dp[i] 为 0~i 所需要的操作数,cnt为1的个数
s[i] == 1 dp[i] = dp[i-1]
s[i] == 0 && s[i-1] == 0 dp[i] = dp[i-1]
s[i] == 0 && s[i-1] == 1 dp[i] = dp[i-1] + cnt
class Solution {
public:
int maxOperations(string s) {
vector<int> dp(s.size());
dp[0]=0;
int cnt=s[0]-'0';
for(int i=1;i<s.size();i++){
if(s[i]=='1'){
dp[i]=dp[i-1];
cnt++;
}else if( s[i-1] == '0' ){
dp[i]=dp[i-1];
}else{
dp[i]=dp[i-1]+cnt;
}
}
return dp[s.size()-1];
}
};
贪心
遇到连续的0就把前面的1全部向右移
class Solution {
public:
int maxOperations(string s) {
int ans=0,cnt=s[0]-'0';
for(int i=1;i<s.size();i++){
if(s[i]=='1'){
cnt++;
}else if(s[i]=='0' && s[i-1]=='1'){
ans+=cnt;
}
}
return ans;
}
};
将num变到target 和 0 变到num-target 是一样的,我们将问题转化到将0 变成num-target。
由于是在连续区间加上或减去1,可以利用差分数组来做,在l~r区间上+1,我们可以在i位置+1,并在r+1位置-1,最后求一个前缀和就得到了目标数组。
例如数组长度为5,我们要先在0 ~ 5上+1,再2 ~ 4上+1
差分数组 [1,1,0,0,-1,-1]
前缀和数组 [1,2,2,2,1]
nums = [3,5,1,2], target = [4,6,2,4] 的差分数组是 [1,0,0,1],由于差分数组的性质,前面有+1,后面就要有-1,差分数组是要比原数组多一个位置的,所以+1 和 -1的数量可能对不上。我们可以换一种理解方式,前面有一个+1,那么我们就获得了在后面的位置减一次1的机会,如果后面有-1,那么我们就使用这次机会。
设s为前面贡献给后面的操作,d为当前位置的数,ans为总的操作数
1.s == 0 ans += abs(d)
2.s > 0 d>0 && s>=d ans 不变 s -= d
d>0 && s< d ans += abs(s-d) s = 0
d<0 ans += abs(d) s -= d
3.s < 0 d<0 && s<=d ans 不变 s -= d
d<0 && s> d ans += abs(s-d) s = 0
d>0 ans += abs(d) s -= d
class Solution {
public:
long long minimumOperations(vector<int>& nums, vector<int>& target) {
vector<int> arr(nums.size());
for(int i=0;i<nums.size();i++){
arr[i]=target[i]-nums[i];
}
for(int i=nums.size()-1;i>0;i--){
arr[i]=arr[i]-arr[i-1];
}
int s=-arr[0];
long long ans=abs(arr[0]);
for(int i=1;i<arr.size();i++){
int d=arr[i];
if ((s<0 && d>0) || (s>0 && d<0) || s==0 ){
ans += abs(d);
}else if( (0<s && s<d) || (s<0 && s>d) ){
ans += abs(s-d);
}
s -= d;
}
return ans;
}
};
另一种方法就是补出差分数组的多余的位置,nums = [3,5,1,2], target = [4,6,2,4] 的差分数组是 [1,0,0,1,-2]。假设我们进行的+1操作为n,为后面贡献的-1个数也为n,-1操作为m,为后面贡献的+1个数也为m。总操作数显然为n+m,而差分数组中1的个数为我们操作加的1 和 -1为后面贡献的+1的和即n+m。也就是说总操作数就是差分数组中正数的和。
class Solution {
public:
long long minimumOperations(vector<int>& nums, vector<int>& target) {
vector<int> arr(nums.size()+1,0);
for(int i=0;i<nums.size();i++){
arr[i]=target[i]-nums[i];
}
for(int i=arr.size()-1;i>0;i--){
arr[i]=arr[i]-arr[i-1];
}
long long ans=0;
for(int i=0;i<arr.size();i++){
if(arr[i]>0) ans += arr[i];
}
return ans;
}
};