Leetcode周赛369总结

Q1 找出数组中的K-or值

  • 题目链接

  • 解题思路:

    • 检查0-31位
    • 如果对于某一位i,存在至少k个元素该位为1,则将2的i次幂加到答案中
  • 解题代码:

class Solution {
public:
    int findKOr(vector<int>& nums, int k) {
        int ans = 0;
        for(int i = 0; i < 31; i++)
        {
            int cnt = 0;
            for(auto num : nums)
            {
                cnt += (num >> i) & 1;
                if(cnt >= k)
                {
                    ans |= (1 << i);
                    break;
                }
            }
        }
        return ans;
    }
};

Q2 数组的最小相等和

  • 题目链接

  • 解题思路:

    • 首先分别统计两个数组的和,以及其中分别包含多少个0
    • 计算数组和 + 0的个数,即分别得到两个数组各自的最小和是多少
      • 如果相等,返回该值
      • 如果不相等:
        • 较小值对应的数组0的个数为0,返回-1
        • 返回较大值
  • 解题代码:

class Solution {
public:
    long long minSum(vector<int>& nums1, vector<int>& nums2) {
        long long ans = 0;
        long long sum1 = 0, sum2 = 0;
        int cnt1 = 0, cnt2 = 0;
        for(auto num : nums1)
        {
            sum1 += num;
            cnt1 += num == 0;
        }
        for(auto num : nums2)
        {
            sum2 += num;
            cnt2 += num == 0;
        }
        sum1 += cnt1;
        sum2 += cnt2;
        if(sum1 == sum2)
            return sum1;
        if(sum1 < sum2 && cnt1 == 0 || sum2 < sum1 && cnt2 == 0)
            return -1;
        return max(sum1, sum2);
    }
};
//调库
class Solution {
public:
    long long minSum(vector<int>& nums1, vector<int>& nums2) {
        long long ans = 0;
        long long sum1 = 0, sum2 = 0;
        int cnt1 = 0, cnt2 = 0;
        sum1 = accumulate(nums1.begin(), nums1.end(), 0ll);
        sum2 = accumulate(nums2.begin(), nums2.end(), 0ll);
        cnt1 = count(nums1.begin(), nums1.end(), 0);
        cnt2 = count(nums2.begin(), nums2.end(), 0);
        sum1 += cnt1;
        sum2 += cnt2;
        if(sum1 == sum2)
            return sum1;
        if(sum1 < sum2 && cnt1 == 0 || sum2 < sum1 && cnt2 == 0)
            return -1;
        return max(sum1, sum2);
    }
};

Q3 使数组变美的最小增量运算数

  • 题目链接

  • 解题思路:

    • 对于以i位置结尾的子数组,只需考虑i-2、i-1、i三个位置的数中,将哪个数变成大于等于k的数
    • 定义dfs(i),表示将[0, i]中的数变成美丽数组需要多少次递增运算
    • 状态转移:
      • 如果修改i-2位置的数,转移到dfs(i+1) + max(0, k - nums[i-2])
      • 如果修改i-1位置的数,转移到dfs(i+2) + max(0, k - nums[i-1]) 为什么是i+2呢,因为dfs(i+1)需要考虑的是i-1, i, i+1位置,而i-1位置已经被修改成了满足大于等于k的数,因此不需要再进行任何修改
      • 如果修改i位置的数,转移到dfs(i+3) + max(0, k - nums[i]) 原因同上
      • dfs(i) 等于上述三种情况中的最小值
  • 解题代码:

class Solution {
public:
    long long minIncrementOperations(vector<int>& nums, int k) {
        int n = nums.size();
        vector<long long> f(n, -1);
        function<long long(int)> dfs = [&](int i) -> long long
        {
            if(i >= n)
                return 0;
            if(f[i] != -1)
                return f[i];
            long long &ans = f[i];
            ans = LONG_LONG_MAX;
            ans = min(ans, dfs(i+1) + max(0, k - nums[i-2]));
            ans = min(ans, dfs(i+2) + max(0, k - nums[i-1]));
            ans = min(ans, dfs(i+3) + max(0, k - nums[i]));
            return ans;
        };
        return dfs(2);
    }
};

  • 另一种思路:

    • 定义dfs(i, j),表示数组[0, i]位置,且右侧有j个小于k的数时,需要的最小运算数

    • 状态转移:

      • 选 dfs(i, j) = dfs(i-1, 0) + max(k - nums[i], 0)
      • 不选 dfs(i, j) = dfs(i-1, j+1),需要满足 j < 2
      • 转移到两者中较小值
    • 递归入口:dfs(n-, 0)

  • 解题代码:

class Solution {
public:
    long long minIncrementOperations(vector<int>& nums, int k) {
       
        int n = nums.size();
       
        vector<vector<long long>> f(n, vector<long long>(3, -1));
        function<long long(int i, int j)> dfs = [&](int i, int j) -> long long
        {
            if(i < 0)
                return 0;
            if(f[i][j] != -1)
                return f[i][j];
            long long &ans = f[i][j];
            ans = dfs(i-1, 0) + max(k - nums[i], 0);
            if(j < 2 && nums[i] < k)
                ans = min(ans, dfs(i-1, j+1));
            return ans;
        };
        return dfs(n-1, 0);
    }
};
  • 改递推:
class Solution {
public:
    long long minIncrementOperations(vector<int>& nums, int k) {
       
        int n = nums.size();
   
        vector<vector<long long>> f(n+1, vector<long long>(3, 0));
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j < 3; j++)
            {
                f[i][j] = f[i-1][0] + max(k - nums[i-1], 0);
                if(j < 2 && nums[i-1] < k)
                    f[i][j] = min(f[i][j], f[i-1][j+1]);
            }
        }
        return f[n][0];
    }
};
  • 空间优化
class Solution {
public:
    long long minIncrementOperations(vector<int>& nums, int k) {
       
        long long f0 = 0, f1 = 0, f2 = 0;
        for(auto num : nums)
        {
            long long inc = f0 + max(k - num, 0);
            f0 = min(inc, f1);
            f1 = min(inc, f2);
            f2 = inc;
        }
        return f0;
    }
};

Q4 收集所有金币可获得的最大积分

  • 题目链接

  • 解题思路:

    • 首先建图

    • 定义dfs(i, fa, cnt),表示从fa经过cnt次第二种方法遍历到i节点,遍历以i节点为根节点的字数可以获得的最大积分

    • 状态转移:

      • 如果在i节点选择用第一种方式,转移到sum(dfs(y, i, cnt)) + coins[i] >> cnt - k,其中y是i的子节点
      • 如果在i节点选择用第二种方式,转移到sum(dfs(y, i, cnt+1) + coins[i] >> (cnt+1), 其中y是i的子节点
    • 本题要注意的点是,各节点金币数最大为1e4,这意味着当cnt >= 14时,数中所有节点都会变为0,此时最大值只能是0,不用再向下搜索

  • 解题代码:


class Solution {
public:
    int maximumPoints(vector<vector<int>>& edges, vector<int>& coins, int k) {
       
        int n = edges.size() + 1;
        vector<vector<int>> g(n);
        for(auto &edge : edges)
        {
            int x = edge[0];
            int y = edge[1];
            g[x].push_back(y);
            g[y].push_back(x);
        }
        vector<vector<int>> f(n, vector<int>(14, -1));
        function<int(int, int, int)> dfs = [&](int i, int fa, int cnt) -> int
        {
            if(cnt >= 14)
                return 0;
            if(f[i][cnt] != -1)
                return f[i][cnt];
            int &ans = f[i][cnt];
            int coin = coins[i] >> cnt;
            int ans1 = coin - k;
            for(auto y : g[i])
            {
                if(y != fa)
                    ans1 += dfs(y, i, cnt);
            }
            int ans2 = coin >> 1;
            for(auto y : g[i])
            {
                if(y != fa)
                    ans2 += dfs(y, i, cnt+1);
            }
            ans = max(ans1, ans2);
            return ans;
        };
        return dfs(0, -1, 0);
    }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值