力扣周赛 - 441

🔗 https://leetcode.cn/discuss/post/3613792/di-441-chang-li-kou-zhou-sai-by-leetcode-qc85/

力扣 3487. 删除后的最大子数组元素和

🔗 https://leetcode.cn/problems/maximum-unique-subarray-sum-after-deletion/description/

题目

  • 给一个数组 nums,里面的数字有正有负
  • 求 nums 子数组的最大和,要求是子数组的元素各不相同,子数组不为空

思路

  • 若数组都是负数,返回最大值
  • 若数组有正有负,对正数去重后求和

代码

class Solution {
public:
    int maxSum(vector<int>& nums) {

        int ans = nums[0];
        bool mark = false;
        for (auto num : nums) {
            if (num > 0) { mark = true; break; }
            ans = max(ans, num);
        }

        if (mark == false) {
            return ans;
        }

        set<int> s;
        ans = 0;
        for (auto num : nums) {
            if (num <= 0) continue;
            if (s.count(num)) continue;
            ans += num;
            s.insert(num);
        }
        return ans;

    }
};

力扣 3488. 距离最小相等元素查询

🔗 https://leetcode.cn/problems/closest-equal-element-queries/description/

题目

  • 给一个循环数组 nums,给一个查询数组 querys
  • 对每一个 query,返回距离当前 num[query] 最近的相等元素,返回其 distance,若不存在返回 -1

思路

  • 下标从小到大,dis[i] = min(i - mp[num], n - i + mp_m[num])
  • 下标从大到小,dis[i] = min(mp[num] - i, i + n - mp_m[num])
  • mp 记录按顺序遍历,最近的 num 的下标,mp_m 记录 num 第一次出现的下标

代码

class Solution {
public:
    vector<int> solveQueries(vector<int>& nums, vector<int>& queries) {
        int dis[100100];
        map<int, int> mp;
        map<int, int> mp_m;
        int n = nums.size();
        for (int i = 0; i < nums.size(); i++) {
            int num = nums[i];
            if (mp.count(num) == 0) {
                dis[i] = -1;
                mp[num] = i;
                mp_m[num] = i;
                continue;
            }
            dis[i] = min(i - mp[num], n - i + mp_m[num]);
            mp[num] = i;
        }
        
        mp.clear();
        mp_m.clear();
        
        for (int i = n - 1; i >= 0; i--) {
            int num = nums[i];
            if (mp.count(num) == 0) {
                mp[num] = i;
                mp_m[num] = i;
                continue;
            }
            
            int tmp = min(mp[num] - i, i + n -  mp_m[num]);
            if (dis[i] == -1) {
                dis[i] = tmp;
            } else {
                dis[i] = min(dis[i], tmp);
            }
            mp[num] = i;
        }
        
        vector<int> ans;
        for (auto query : queries) {
            ans.push_back(dis[query]);
        }
        return ans;
        
    }
};

力扣 3489. 零数组变换 IV

🔗 https://leetcode.cn/problems/zero-array-transformation-iv/description/

题目

  • 给一个数组 nums,给一个操作数组 querys
  • 操作数组由三个数字组成,l r 和 val,表明 num 数组的 l 到 r 之间,可选若干元素,减少 val 值,nums[index] 需要可以被 val 正好减掉,即 num[index] 需要大于等于 val 才可以执行此操作,
  • 返回按照顺序经过多少个 query 操作,可以使得 nums 为全零数组,若不行返回 -1

思路

  • 问题可以转化为,对于每个 num,按照顺序经过多少个 query 操作,可以变为 0,返回每个 num 操作中的最大值;若有一个 num 无法变为 0,返回 -1
  • 对于每个 num 的 query 操作,可以转化为 dp 背包,即当前 val 用或不用,可以使得 val 和为 num,这个可以参考 :https://www.notion.so/pursuitcane/416-19db2ee0934c808d94cfdb372eeed777

代码

class Solution {
public:
    int minZeroArray(vector<int>& nums, vector<vector<int>>& queries) {
        int ans = 0;
        for (int i = 0; i < nums.size(); i++) {
            int num = nums[i];
            if (num == 0) continue;
            bool f[1010];
            memset(f, 0, sizeof f);
            f[0] = true;
            
            int k = -1;
            for (int j = 0; j < queries.size(); j++) {
                int l = queries[j][0];
                int r = queries[j][1];
                int val = queries[j][2];
                if (l > i || r < i) continue;

                for (int sum = num; sum >= val; sum--) {
                    if (f[sum - val]) f[sum] = true;
                }
                if (f[num]) {
                    k = j + 1;
                    break;
                }
            }

            if (k == -1) return -1;
            ans = max(ans, k);
        }
        return ans;

    }
};

力扣 3490. 统计美丽整数的数目

🔗 https://leetcode.cn/problems/count-beautiful-numbers/description/

题目

  • 返回区间 [l, r] 之间的美丽整数的个数
  • 美丽整数的定义是:该正整数每一位的 相乘 可以被 每一位的 sum 整除

思路

  • 数位 dp,代码没有套模板,所以会有点啰嗦和繁琐
  • 计算 cal(x),表示 [1, x] 区间的美丽整数
  • 题目的答案即为 cal® - cal(l-1),即 [1,r] 区间的美丽整数 - [1, l-1] 区间的美丽整数
  • 计算 cal(x),用到的状态转移方程为:
    • dp[index][mul][sum] += dp[index + 1][mul * num][sum + num]
    • 枚举当前位置放置的数字 num,递归放置下一位 index,传递 mul * num,sum * num 的状态
    • 当 index 大于枚举位,此时 mul % sum == 0,则进行统计
  • 这里有两种情况需要考虑
    • is_limit,枚举当前位置是可以没有限制,从 0 到 9,还是需要考虑当前位置的最大值
      • 若当前位置没有限制,则枚举 0-9,后续的位置也没有限制
      • 若当前的位置有限制,则枚举 0-最大值-1 且后续的位置没有限制 + 最大值且后续位置有限制最大值
    • is_num,对于前导 0 的情况,mul 初始值为 1,不可以直接乘以 0
      • 有 is_limit 时,必定不是前导 0 的情况
  • 这里必然有重复计算,通过记忆搜索记录中间可复用的结果
    • cache 的存储和使用条件为:当前是一个数字,而不是前导 0 的枚举,即 is_num == true;当前位置没有限制,可以从 0 到 9,即 is_limit == false
    • cache 的 key 设计为:pair<mul, sum, n - index>

代码

class Solution {
public:
    map<uint64_t, int> cache;
    int dp(string& upper_s, int index, int mul, int sum, bool is_limit, bool is_num) {
        int n = upper_s.size();
        if (index == n) {
            if (sum && mul % sum == 0)
                return 1;
            return 0;
        }

        
        int ret = 0;

        if (is_limit == false) {
            uint64_t mask = ((uint64_t)mul << 32) + (sum << 16) + n - index;
            if (is_num) {
                if (cache.count(mask)) return cache[mask];
                for (int i = 0; i < 10; i++) {
                    ret += dp(upper_s, index + 1, mul * i, sum + i, 0, 1);
                }
                cache[mask] = ret;

            } else {
                ret = dp(upper_s, index + 1, mul, sum, 0, 0);            
                for (int i = 1; i < 10; i++) {
                    ret += dp(upper_s, index + 1, mul * i, sum + i, 0, 1);
                }

            }
        }

        if (is_limit) {
            int dig = upper_s[index] - '0';
            for (int i = 0; i < dig; i++) {
                ret += dp(upper_s, index + 1, mul * i, sum + i, 0, 1);
            }
            ret += dp(upper_s, index + 1, mul * dig, sum + dig, 1, 1);
        }

        return ret;
    }
    int cal(int upper) {
        string upper_s = to_string(upper);
        int ret = 0;
        int dig = upper_s[0] - '0';
        // curr is 0,前导 0, 后续可以 0-9 的统计
        ret += dp(upper_s, 1, 1, 0,  /* is_limit*/false, /* is_num */ false); 
        for (int i = 1; i < dig; i++) { 
            // curr 1 ~ dig, 后续可以 0-9 的统计
            ret += dp(upper_s, 1, i, i,  /* is_limit*/false, /* is_num */ true);
        }
        // curr dig, 后续需要考虑 upper
        ret += dp(upper_s, 1, dig, dig,  /* is_limit*/true, /* is_num */  true); 
        return ret;
    }
    int beautifulNumbers(int l, int r) { return cal(r) - cal(l - 1); }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值