返回顶部

LeetCode第280场周赛题解

6004. 得到 0 的操作数

题目描述:给你两个非负整数,使用辗转相减法,求最大公约数,问相减的次数。

思路:根据题意模拟即可。

时间复杂度:\(O(n)\)\(n\)为数字范围

参考代码:

class Solution {
public:
    int countOperations(int num1, int num2) {
        int res = 0;
        while(num1 && num2){
            if(num1 >= num2) num1 -= num2;
            else num2 -= num1;
            ++res;
        }
        return res;
    }
};

6005. 使数组变成交替数组的最少操作数

题目描述:给你一个数组,让你将奇数下标和偶数下标的数字分别变相等但相邻数字不同的最小操作次数

思路:明显的贪心,根据情况讨论即可

时间复杂度:\(O(n)\)

参考代码:

class Solution {
public:
    int minimumOperations(vector<int>& nums) {
       vector<int>cnt0(100005 , 0), cnt1(100005 , 0);
        int n = nums.size();
        int p = 0 , q = 0;
        for(int i = 0 ; i < n ; ++i){
            if(i & 1) cnt1[nums[i]]++, ++q;
            else cnt0[nums[i]]++, ++p;
        }
        int res = INT_MAX;
        vector<vector<int>> idx0(2 , vector<int>(2 , 0)), idx1(2 , vector<int>(2 , 0));
        for(int i = 1 ; i < 100005 ; ++i){
            if(cnt0[i] != 0){
                if(cnt0[i] > idx0[1][1]){
                    idx0[0][1] = idx0[1][1];
                    idx0[0][0] = idx0[1][0];
                    idx0[1][0] = i;
                    idx0[1][1] = cnt0[i];
                }
                else if(cnt0[i] > idx0[0][1]){
                    idx0[0][1] = cnt0[i];
                    idx0[0][0] = i;
                }
            }
            if(cnt1[i] != 0){
                if(cnt1[i] > idx1[1][1]){
                    idx1[0][1] = idx1[1][1];
                    idx1[0][0] = idx1[1][0];
                    idx1[1][0] = i;
                    idx1[1][1] = cnt1[i];
                }
                else if(cnt1[i] > idx1[0][1]){
                    idx1[0][1] = cnt1[i];
                    idx1[0][0] = i;
                }
            }
        }
        if(idx0[1][0] != idx1[1][0]) res = p - idx0[1][1] + q - idx1[1][1];
        else{
            if(idx0[0][0] == 0 && idx1[0][0] == 0) res = min(p , q);
            else if(idx0[0][0] == 0) res = min(idx0[1][1] + q - idx1[1][1] , q - idx1[0][1]);
            else if(idx1[0][0] == 0) res = min(idx1[1][1] + p - idx0[1][1] , p - idx0[0][1]);

            else res = min(p - idx0[0][1] + q - idx1[1][1] , p - idx0[1][1] + q - idx1[0][1]);
        }
        return res;
    }
};

6006. 拿出最少数目的魔法豆

题目描述:给你一个 正 整数数组 beans ,其中每个整数表示一个袋子里装的魔法豆的数目。请你从每个袋子中 拿出 一些豆子(也可以 不拿出),使得剩下的 非空 袋子中(即 至少 还有 一颗 魔法豆的袋子)魔法豆的数目 相等 。一旦魔法豆从袋子中取出,你不能将它放到任何其他的袋子中。请你返回你需要拿出魔法豆的 最少数目。

思路:比较明显的\(dp\),假设我们的目标值是\(x\),那么对于小于\(x\)的袋子里面的魔法豆要全部拿出,对于大于\(x\)的袋子的魔法豆要拿出一部分。

定义状态\(f_i\)表示小于等于\(x\)的袋子的魔法豆数量的总和。定义状态\(g_i\)表示所有袋子的魔法豆减小到不超过\(x\)需要拿出的魔法豆的数量,则转移方程为:

\[f_i = f_{i - 1} + i * cnt_i\\ g_i = g_{i + 1} + sum \]

其中\(cnt_i\)表示魔法豆数量为\(i\)的袋子的数量,\(sum = \sum\limits_{j = i + 1}^{N}cnt_j\)

最终答案就是\(\mathop{min}\limits_{i = 1}^{N} f_{i - 1} + g_i\)

时间复杂度:\(O(n)\)

参考代码:

class Solution {
public:
    long long minimumRemoval(vector<int>& beans) {
        const int N = 100005; 
        vector<int>cnt(100005 , 0);
        for(auto bean : beans) cnt[bean]++;
        long long res = LLONG_MAX;
        vector<long long> f(N , 0), g(N , 0);
        for(int i = 1 ; i < N ; ++i) f[i] = f[i - 1] + 1ll * i * cnt[i];
        int sum = 0;
        for(int i = N - 2 ; i >= 1 ; --i) {
            g[i] = g[i + 1] + sum;
            sum += cnt[i];
        }
        for(int i = 1 ; i < N - 4 ; ++i) res = min(res , f[i - 1] + g[i]);
        return res;
    }
};

6007. 数组的最大与和

题目描述:有\(m\)个桶从\(1\)开始编号,再给你一个长度为\(n\)的数组\(nums\),让你将这些数字放入任意一个桶中,但桶中的数字不超过\(2\)个。对于每一个数字贡献为桶的编号\(idx\)与该值的AND,假设这个数字是\(nums_i\)则贡献为\(nums_i \& idx\)。问最大的贡献和是多少。

数据范围:\(1 \leq m \leq 9 , 1 \leq n \leq 2 * m\)

思路:考虑到数据范围很小,比较容易想到爆搜或者状压\(dp\)。由于每个桶中的元素个数不超过\(2\),故我们把桶的数量乘以\(2\),然后每个桶的编号就是\(\frac{idx}{2} + 1\)。这样每个桶中最多放一个元素。当数字\(nums_i\)放入桶\(idx\)中时,对答案的贡献为\((\frac{idx}{2} + 1) \& nums_i\)。我们考虑状压\(dp\),定义状态\(f_i\)表示枚举第\(count_i\)个数字时的最大价值。\(count_i\)表示\(i\)中二进制位为\(1\)的数量。那么对于每一个\(i\)中二进制位为\(0\)的位置都可以计算价值。转移方程为:

\[f_{i + 2^j} = f_i + ((\frac{j}{2} + 1) \& nums_k) \]

其中\((i >> j \&1) == 0\),\(k = count_i\)

时间复杂度:\(O(2m \times 2^{2m})\)

参考代码:

class Solution {
private:
    int calBit(int x){
        int cnt = 0;
        for(int i = 20 ; i >= 0 ; --i) cnt += (x >> i) & 1;
        return cnt;
    }
public:
    int maximumANDSum(vector<int>& nums, int numSlots) {
        numSlots <<= 1;
        vector<int>f(1 << numSlots, 0);
        int n = nums.size(), m = 1 << numSlots;
        int res = 0;
        for(int i = 0 ; i < m ; ++i){
            int dx = calBit(i);
            if(dx >= n) continue;
            for(int j = 0 ; j < numSlots; ++j){
                if((i >> j) & 1) continue;
                int k = i ^ (1 << j);
                f[k] = max(f[k] , f[i] + ((j / 2 + 1) & nums[dx]));
                res = max(res , f[k]);
            } 
        }
        return res;
    }
};
posted @ 2022-02-13 13:02  cherish-lgb  阅读(63)  评论(0编辑  收藏  举报