单调队列优化dp+位运算,LeetCode 3117. 划分数组得到最小的值之和

目录

一、题目

1、题目描述

2、接口描述

cpp

3、原题链接

二、解题报告

1、思路分析

2、复杂度

3、代码详解

cpp


一、题目

1、题目描述

给你两个数组 numsandValues,长度分别为 nm

数组的 等于该数组的 最后一个元素。

你需要将 nums 划分为 m不相交的连续

子数组
,对于第 ith 个子数组 [li, ri],子数组元素的按位 AND 运算结果等于 andValues[i],换句话说,对所有的 1 <= i <= mnums[li] & nums[li + 1] & ... & nums[ri] == andValues[i] ,其中 & 表示按位 AND 运算符。

返回将 nums 划分为 m 个子数组所能得到的可能的 最小 子数组 之和。如果无法完成这样的划分,则返回 -1

2、接口描述

cpp
复制代码
 
cpp 复制代码
class Solution {
public:
    int minimumValueSum(vector<int>& nums, vector<int>& andValues) {
        
    }
};

3、原题链接

3117. 划分数组得到最小的值之和


二、解题报告

1、思路分析

很明显的dp,我们不难想到定义状态f(j, i) 为 前 i 个数 划分为 j 段的最大收益

但是如何进行状态转移,换言之,固定i,如何找到k所在区间使得and(l, r) = andValuesj

我们可以二分找左端点,也可以利用 固定一个端点的 前缀位或和 最多有log种取值的性质来获取这个区间

假如区间为l, r,那么我们fj, i = max(fj - 1, k + numsi)

直接枚举是会超时的

由于固定andValuesj,随着 i 的右移,l 也会移动,这就变成了一个不断右移的滑窗,我们可以单调队列维护最值,就把枚举区间内每一个位置变成了均摊O(1)

然后就可以做了

2、复杂度

时间复杂度: O(N M logU)空间复杂度:O(N)

3、代码详解

cpp
cpp 复制代码
class Solution {
public:
    int minimumValueSum(vector<int>& nums, vector<int>& andValues) {
        const int inf = 1'000'000'007;
        int n = nums.size();
        std::vector<int> f(n + 1, inf), nf(n + 1);

        f[0] = 0;
        for (int target : andValues) {
            std::vector<std::pair<int, int>> a;
            std::deque<int> dq;
            int qi = 0;

            nf[0] = inf;
            for (int i = 0; i < n; ++ i) {
                int x = nums[i];
                for (auto &[_and, _] : a)
                    _and &= x;
                a.emplace_back(x, i);

                int j = 0, last = -1;
                for (auto &[and_, _] : a) {
                    if (and_ >= target && and_ != last) {
                        a[j ++] = {and_, _};
                        last = and_;
                    }
                }
                a.resize(j);

                if (a.size() && a[0].first == target) {
                    int r = a.size() > 1 ? a[1].second - 1 : i;

                    for (; qi <= r; ++ qi) {
                        while (dq.size() && f[qi] <= f[dq.back()])
                            dq.pop_back();
                        dq.push_back(qi);
                    }

                    while (dq.front() < a[0].second)
                        dq.pop_front();
                    
                    nf[i + 1] = f[dq.front()] + x;
                } 
                else {
                    nf[i + 1] = inf;
                }
            }
            std::swap(f, nf);
        }
        return f[n] < inf ? f[n] : -1;
    }
};
相关推荐
码完就睡4 分钟前
数据结构——串的模式匹配算法(BF算法、KMP算法)
数据结构·算法
tachibana24 分钟前
hot100 排序链表(148)
java·数据结构·算法·leetcode·链表
林泽毅11 分钟前
Qwen3.5 DPO 模型对齐实战(pytrio训练版)
人工智能·python·算法
imuliuliang17 分钟前
算法中的随机化思想及其复杂度收益评估的技术7
算法
不能跑的代码不是好代码38 分钟前
二叉树从基础概念到LeetCode实战
算法·leetcode
凯瑟琳.奥古斯特1 小时前
二分查找解力扣1011最优运载能力
开发语言·c++·算法·leetcode·职场和发展
学计算机的计算基1 小时前
二叉树算法下篇:递归核心技巧与高频面试题详解
java·笔记·算法
水龙吟啸1 小时前
华为2026.6.24机考选择题+编程题【速刷敲黑板】
人工智能·深度学习·算法·机器学习·华为
Eloudy1 小时前
全文 - Evolution Strategies as a Scalable Alternative to Reinforcement Learning
人工智能·算法
net3m332 小时前
可以加大adc滑动滤波的间隔时间,比如定时每20ms采集一次滤波输入,且用滑动窗口均值滤波
算法