力扣周赛407题解

32分钟3题

t1:3226. 使两个整数相等的位更改次数

直接模拟
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;
    }
};

t2:3227. 字符串元音游戏

设字符串中元音的个数为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;
    }
};

t3:3228. 将 1 移动到末尾的最大操作次数

最小操作数是从右往左遍历,遇到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;
    }
};

t4:3229. 使数组等于目标数组所需的最少操作次数

将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;
    }
};
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值