【LeetCode周赛】第369场周赛

LeetCode第369场周赛

  • 100111. 找出数组中的 K-or 值 简单
  • 100102. 数组的最小相等和 中等
  • 100107. 使数组变美的最小增量运算数 中等

100111. 找出数组中的 K-or 值 简单

给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。

nums 中的 K-or 是一个满足以下条件的非负整数:

只有在 nums 中,至少存在 k 个元素的第 i 位值为 1 ,那么 K-or 中的第 i 位的值才是 1 。
返回 nums 的 K-or 值。

注意 :对于整数 x ,如果 ( 2 i 2^i 2i AND x) == 2 i 2^i 2i ,则 x 中的第 i 位值为 1 ,其中 AND 为按位与运算符。

示例 1

输入:nums = [7,12,9,8,9,15], k = 4
输出:9
解释:nums[0]、nums[2]、nums[4] 和 nums[5] 的第 0 位的值为 1 。
nums[0] 和 nums[5] 的第 1 位的值为 1 。
nums[0]、nums[1] 和 nums[5] 的第 2 位的值为 1 。
nums[1]、nums[2]、nums[3]、nums[4] 和 nums[5] 的第 3 位的值为 1 。
只有第 0 位和第 3 位满足数组中至少存在 k 个元素在对应位上的值为 1 。因此,答案为 2^0 + 2^3 = 9 。

示例 2

输入:nums = [2,12,1,11,4,5], k = 6
输出:0
解释:因为 k == 6 == nums.length ,所以数组的 6-or 等于其中所有元素按位与运算的结果。因此,答案为 2 AND 12 AND 1 AND 11 AND 4 AND 5 = 0 。

示例 3

输入:nums = [10,8,5,9,11,6,8], k = 1
输出:15
解释:因为 k == 1 ,数组的 1-or 等于其中所有元素按位或运算的结果。因此,答案为 10 OR 8 OR 5 OR 9 OR 11 OR 6 OR 8 = 15 。

提示:

  • 1 < = n u m s . l e n g t h < = 50 1 <= nums.length <= 50 1<=nums.length<=50
  • 0 < = n u m s [ i ] < 2 31 0 <= nums[i] < 2^{31} 0<=nums[i]<231
  • 1 < = k < = n u m s . l e n g t h 1 <= k <= nums.length 1<=k<=nums.length

分析:

将数组的所有数转换成二进制,统计对应二进制位为1的数量。

代码:

class Solution {
public:
    int findKOr(vector<int>& nums, int k) {
        vector<int> n(32,0);
        for(int& num : nums){
            int i=0,t=num;
            while(t){
                n[i++]+=t%2;
                t/=2;
            }
        }
        long long ans=0, cnt=1;
        for(int i=0;i<31;i++){
            if(n[i]>=k) ans+=cnt;
            cnt*=2;
        }
        return ans;
    }
};


100102. 数组的最小相等和 中等

给你两个由正整数和 0 组成的数组 nums1 和 nums2 。

你必须将两个数组中的 所有 0 替换为 严格 正整数,并且满足两个数组中所有元素的和 相等 。

返回 最小 相等和 ,如果无法使两数组相等,则返回 -1 。

示例 1

输入:nums1 = [3,2,0,1,0], nums2 = [6,5,0]
输出:12
解释:可以按下述方式替换数组中的 0 :

  • 用 2 和 4 替换 nums1 中的两个 0 。得到 nums1 = [3,2,2,1,4] 。
  • 用 1 替换 nums2 中的一个 0 。得到 nums2 = [6,5,1] 。
    两个数组的元素和相等,都等于 12 。可以证明这是可以获得的最小相等和。

示例 2

输入:nums1 = [2,0,2,0], nums2 = [1,4]
输出:-1
解释:无法使两个数组的和相等。

提示:

1 < = n u m s 1. l e n g t h , n u m s 2. l e n g t h < = 1 0 5 1 <= nums1.length, nums2.length <= 10^5 1<=nums1.length,nums2.length<=105
0 < = n u m s 1 [ i ] , n u m s 2 [ i ] < = 1 0 6 0 <= nums1[i], nums2[i] <= 10^6 0<=nums1[i],nums2[i]<=106

分析:

分别统计nums1和nums2的和以及0的数目。
据题意可得,只要二者都有0,一定能得到结果。
假设所有的0都变成1,与原来的和相加得到sum1和sum2:

  • 如果sum1更大,且nums2不存在0(包含了二者都不包含0),则一定满足不了,返回 -1
  • 如果sum2更大,且nums1不存在0(包含了二者都不包含0),则一定满足不了,返回 -1
  • 其余情况均能满足

代码:

class Solution {
public:
    long long minSum(vector<int>& nums1, vector<int>& nums2) {
        int n1,n2;
        long long sum1,sum2;
        n1=count(nums1.begin(),nums1.end(),0);
        sum1=accumulate(nums1.begin(),nums1.end(),1LL*0)+n1;
        n2=count(nums2.begin(),nums2.end(),0);
        sum2=accumulate(nums2.begin(),nums2.end(),1LL*0)+n2;

        long long t=sum1-sum2;
        cout<<t<<endl;
        if((t>0&&n2==0)||(t<0&&n1==0)) return -1;
        return max(sum1,sum2);
    }
};


100107. 使数组变美的最小增量运算数 中等

给你一个下标从 0 开始、长度为 n 的整数数组 nums ,和一个整数 k 。

你可以执行下述 递增 运算 任意 次(可以是 0 次):

从范围 [0, n - 1] 中选则一个下标 i ,并将 nums[i] 的值加 1 。
如果数组中任何长度 大于或等于 3 的子数组,其 最大 元素都大于或等于 k ,则认为数组是一个 美丽数组 。

以整数形式返回使数组变为 美丽数组 需要执行的 最小 递增运算数。

子数组是数组中的一个连续 非空 元素序列。

示例 1:

输入:nums = [2,3,0,0,2], k = 4
输出:3
解释:可以执行下述递增运算,使 nums 变为美丽数组:
选择下标 i = 1 ,并且将 nums[1] 的值加 1 -> [2,4,0,0,2] 。
选择下标 i = 4 ,并且将 nums[4] 的值加 1 -> [2,4,0,0,3] 。
选择下标 i = 4 ,并且将 nums[4] 的值加 1 -> [2,4,0,0,4] 。
长度大于或等于 3 的子数组为 [2,4,0], [4,0,0], [0,0,4], [2,4,0,0], [4,0,0,4], [2,4,0,0,4] 。
在所有子数组中,最大元素都等于 k = 4 ,所以 nums 现在是美丽数组。
可以证明无法用少于 3 次递增运算使 nums 变为美丽数组。
因此,答案为 3 。

示例 2:

输入:nums = [0,1,3,3], k = 5
输出:2
解释:可以执行下述递增运算,使 nums 变为美丽数组:
选择下标 i = 2 ,并且将 nums[2] 的值加 1 -> [0,1,4,3] 。
选择下标 i = 2 ,并且将 nums[2] 的值加 1 -> [0,1,5,3] 。
长度大于或等于 3 的子数组为 [0,1,5]、[1,5,3]、[0,1,5,3] 。
在所有子数组中,最大元素都等于 k = 5 ,所以 nums 现在是美丽数组。
可以证明无法用少于 2 次递增运算使 nums 变为美丽数组。
因此,答案为 2 。

示例 3:

输入:nums = [1,1,2], k = 1
输出:0
解释:在这个示例中,只有一个长度大于或等于 3 的子数组 [1,1,2] 。
其最大元素 2 已经大于 k = 1 ,所以无需执行任何增量运算。
因此,答案为 0 。

提示:

  • 3 < = n = = n u m s . l e n g t h < = 1 0 5 3 <= n == nums.length <= 10^5 3<=n==nums.length<=105
  • 0 < = n u m s [ i ] < = 1 0 9 0 <= nums[i] <= 10^9 0<=nums[i]<=109
  • 0 < = k < = 1 0 9 0 <= k <= 10^9 0<=k<=109

分析:

如果nums[i] < k,只需要增量到k即可,如果nums[i]>=k 则不需要增量。
任何长度 大于或等于 3 的子数组,只需考虑长度为3的子数组,其余子数组一定包含了某一个长度为3的子数组。
设dp[i]:选择nums[i]进行增量,此前0 ~ i-1已经是美丽数组了,将nums[i]增量之后,0 ~ i 均为美丽数组。此时要保证每三个连续的数中有nums[j] >= k,前面的0 ~ i-1已经满足了美丽数组了,要使新增加的这个数增量之后,整个0 ~ i数组均为美丽数组有以下三种方式:

  • nums[i-3] 增量,nums[i]增量
  • nums[i-2] 增量,nums[i]增量
  • nums[i-1] 增量,nums[i]增量

此时状态转移方程为:
d p [ i ] = m a x ( k − n u m s [ i ] , 0 ) + m i n ( d p [ i − 1 ] , m i n ( d p [ i − 2 ] , d p [ i − 3 ] ) ) dp[i] = max(k-nums[i], 0) + min(dp[i-1], min(dp[i-2], dp[i-3])) dp[i]=max(knums[i],0)+min(dp[i1],min(dp[i2],dp[i3]))
最后,在完全计算所有的dp数组后,从dp[n-1], dp[n-2], dp[n-3] 中选取一个最小的值,因为只要保证最后三个中有一个是满足要求的就好了。

一些思考中的理解:
1、对于状态方程 d p [ i ] = m a x ( k − n u m s [ i ] , 0 ) + m i n ( d p [ i − 1 ] , m i n ( d p [ i − 2 ] , d p [ i − 3 ] ) ) dp[i] = max(k-nums[i], 0) + min(dp[i-1], min(dp[i-2], dp[i-3])) dp[i]=max(knums[i],0)+min(dp[i1],min(dp[i2],dp[i3])),如果选择了dp[i] = max(k-nums[i], 0) + dp[j] (i-3<= j <= i-1),此时该方程代表的意思对nums[i] 和 nums[j]增量,i ~ j之间的都不增量了。
2、对于为什么要从i-1、i-2、i-3中选择一个再加上 max(k-nums[i], 0),明明选择了nums[i-2],此时就可以不选择nums[i]进行增量?

  • 对于选择i-1或者i-2时,可以不选择i这一情况,在后面的dp[i+1]、dp[i+2]、dp[i+3]都会计算到,只要花销更小。
  • 对于为什么选择了i-1或者i-2,还要考虑加上max(k-nums[i], 0)。因为是从dp[i-1]、dp[i-2]、dp[i-3]中选择一个更小的增量值,该转移方程一定会使0 ~ i的数组成为美丽数组,因此只需要考虑花销最小即可。
    可以将nums中的每一个数字的范围辐射到最大,但是他的花销更大,尽管范围更大,但最优方案仍然不是他。
    当然如果i是最后一个,则dp[i]、dp[i-1]、dp[i-2]就会互相比较,选择更小的,此时如果dp[i]是选择了dp[i-1]或dp[i-2],则dp[i]一定会>=min(dp[i-1], dp[i-2]),此时大概率不选择dp[i]。

代码:

记忆化搜索:

class Solution {
public:
    long long dfs(vector<int>& nums,vector<long long>& pre, int i,int k){
        if(i>=nums.size()) return 0;
        if(pre[i]==-1){
            long long cnt=max(k-nums[i],0) + dfs(nums, pre, i+1,k);
            cnt=min(max(k-nums[i],0) + dfs(nums, pre, i+2,k), cnt);
            cnt=min(max(k-nums[i],0) + dfs(nums, pre, i+3,k), cnt);
            pre[i]=cnt;
        }
        return pre[i];
    }

    long long minIncrementOperations(vector<int>& nums, int k) {
        vector<long long> pre(nums.size(),-1);
        return min(dfs(nums,pre,0,k),min(dfs(nums,pre,1,k),dfs(nums,pre,2,k)));
    }
};

动态规划,其实还可以降维:状态转移方程之和前面三个状态有关,只需要常数的空间

class Solution {
public:
    long long minIncrementOperations(vector<int>& nums, int k) {
        vector<long long> dp(nums.size(),0);
        dp[0]=max(k-nums[0],0);dp[1]=max(k-nums[1],0);dp[2]=max(k-nums[2],0);
        for(int i=3;i<nums.size();i++){
            dp[i]=max(k-nums[i],0)+dp[i-3];
            dp[i]=min(dp[i],max(k-nums[i],0)+min(dp[i-1],dp[i-2]));
        }
        return min(dp[nums.size()-1],min(dp[nums.size()-2],dp[nums.size()-3]));
    }
};


你可能感兴趣的:(LeetCode,leetcode,算法,数据结构)