单调队列优化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;
    }
};
相关推荐
徐小夕20 分钟前
表格、文档、甘特、大屏、表单一站打通:pxcharts超级表格4.0正式上线!
前端·算法·github
Xin77026 分钟前
LeetCode 23.合并 K 个升序链表(分治递归)
leetcode
TechLee1 小时前
跨语言加解密总对不上?这个纯 Go 神库让 AES/RSA 与 PHP、Java 100% 互通
java·后端·算法
月华路3 小时前
G1 新生代对象晋升老年代:实现机制与 GC 日志
java·jvm·算法
万法若空3 小时前
排列组合恒等式
c++·算法
Nil2083 小时前
leetcode 105从前序和中序遍历序列构造二叉树
算法·leetcode·职场和发展
一直C3 小时前
【数据结构】哈希表+算法复杂度与经典排序查找(C语言)
java·linux·开发语言·数据结构·算法·ubuntu·散列表
淡海水3 小时前
04-02-哈希-Dictionary-TKey-TValue-上-核心数据结构
数据结构·算法·c#·哈希算法·编译·字典·dictionary
戴西软件4 小时前
国内有哪些数据轻量化格式转换软件?
数据库·算法·安全·信息可视化·自动化·rpa
老洋葱Mr_Onion4 小时前
【C++】CSP-J初赛模拟卷七错题整理(作者自用)
c++·算法·深度优先