单调队列优化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;
    }
};
相关推荐
先吃饱再说14 小时前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰17 小时前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术18 小时前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六1 天前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术1 天前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize1 天前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考2 天前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
美团技术团队2 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode