🔗 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 的情况
- is_limit,枚举当前位置是可以没有限制,从 0 到 9,还是需要考虑当前位置的最大值
- 这里必然有重复计算,通过记忆搜索记录中间可复用的结果
- 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); }
};