单调队列优化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) = andValues[j]

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

假如区间为[l, r],那么我们f[j, i] = max(f[j - 1, k] + nums[i])

直接枚举是会超时的

由于固定andValues[j],随着 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;
    }
};
相关推荐
Xの哲學3 分钟前
Linux 分区表深度技术剖析
linux·网络·算法·架构·边缘计算
写写闲篇儿10 分钟前
经典算法题剖析之传递信息(三)
算法
上不如老下不如小12 分钟前
2025年第七届全国高校计算机能力挑战赛初赛 Python组 编程题汇总
开发语言·python·算法
程序员小白条22 分钟前
你面试时吹过最大的牛是什么?
java·开发语言·数据库·阿里云·面试·职场和发展·毕设
小年糕是糕手33 分钟前
【C++】类和对象(二) -- 构造函数、析构函数
java·c语言·开发语言·数据结构·c++·算法·leetcode
kupeThinkPoem1 小时前
跳表有哪些算法?
数据结构·算法
前端小L1 小时前
图论专题(二十一):并查集的“工程应用”——拔线重连,修复「连通网络」
数据结构·算法·深度优先·图论·宽度优先
88号技师1 小时前
2025年9月一区SCI-孤行尺蠖觅食优化算法Solitary Inchworm Foraging-附Matlab免费代码
开发语言·算法·数学建模·matlab·优化算法
前端小L2 小时前
图论专题(二十五):最小生成树(MST)——用最少的钱,连通整个世界「连接所有点的最小费用」
算法·矩阵·深度优先·图论·宽度优先
前端小L2 小时前
图论专题(二十三):并查集的“数据清洗”——解决复杂的「账户合并」
数据结构·算法·安全·深度优先·图论