【LeetCode周赛】3117. 划分数组得到最小的值之和 | 动态规划 | 困难

题目内容

原题链接

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

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

你需要将 nums 划分为 m 个 不相交的连续 子数组,对于第 ith 个子数组 [li, ri],子数组元素的按位AND运算结果等于 andValues[i],换句话说,对所有的 1 <= i <= m,nums[li] & nums[li + 1] & ... & nums[ri] == andValues[i] ,其中 & 表示按位AND运算符。

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

数据范围

  • 1 <= n == nums.length <= 10000
  • 1 <= m == andValues.length <= min(n, 10)
  • 1 <= nums[i] < 100000
  • 0 <= andValues[j] < 100000

题解

为了方便描述,所有下标都从 1 1 1 开始。

暴力解法

状态定义

考虑 d p [ j ] [ i ] dp[j][i] dp[j][i] 表示考虑前 i i i 个元素,前 j j j 组,第 j j j 组的最后一个元素是 n u m s [ i ] nums[i] nums[i] 的最小子数组值之和。

状态转移

d p [ j ] [ i ] = min ⁡ k = j − 1 i − 1 ( d p [ j − 1 ] [ k ] + n u m s [ i ] ) dp[j][i]=\min\limits_{k=j-1}^{i-1} (dp[j-1][k]+nums[i]) dp[j][i]=k=j1mini1(dp[j1][k]+nums[i])

时间复杂度: O ( 17 n 2 m ) O(17n^2m) O(17n2m)

优化解法1

可以发现的是,从 i i i 往前一直进行与运算,整体是一个单调不增的,所以显然可以二分。

二分出最小的 l l l ,满足 nums[l]&nums[l+1]&...&nums[i]=andValues[j]
二分出最大的 r r r,满足 nums[r]&nums[r+1]&...&nums[i]=andValues[j]

然后 d p [ j ] [ i ] = min ⁡ k = l r ( d p [ j − 1 ] [ k ] + n u m s [ i ] ) dp[j][i]=\min\limits_{k=l}^{r} (dp[j-1][k]+nums[i]) dp[j][i]=k=lminr(dp[j1][k]+nums[i])

这部分的 min ⁡ k = l r ( d p [ j − 1 ] [ k ] ) \min\limits_{k=l}^{r} (dp[j-1][k]) k=lminr(dp[j1][k]) 是可以用一个 RMQ 来维护的。

时间复杂度: O ( 17 n m log ⁡ n ) O(17nm\log n) O(17nmlogn)

优化解法 2

上述因为满足单调性,所以考虑双指针。

整体维护一个 l l l r r r,可以发现的是,当 i i i 递增时, l l l r r r 也是单调不减的,所以类似双指针的做法维护 l l l r r r ,使得满足上述情况即可。

时间复杂度: O ( 17 n m ) O(17nm) O(17nm)

代码

class Solution {
public:
    int minimumValueSum(vector<int>& nums, vector<int>& andValues) {
        const int MAXBIT = 17;
        const int INF = 0x3f3f3f3f;
        int n = nums.size();
        int m = andValues.size();

        nums.insert(nums.begin(), 0);
        andValues.insert(andValues.begin(), 0);
        // dp[j][i] 表示前 j 组和前 i 个元素,第 j 组的最后一个元素是 nums[i] 的情况下的最小权值和
        // dp[j][i] = min(dp[j - 1][k]) + nums[i]
        // 考虑 i ,满足 [k + 1, i]

        vector<vector<int>> pre(MAXBIT, vector<int>(n + 1));
        for (int j = 0; j < MAXBIT; ++j)
            for (int i = 1; i <= n; ++i)
                pre[j][i] = pre[j][i - 1] + (nums[i] >> j & 1);


        vector<vector<int>> dp(m + 1, vector<int>(n + 1, INF));
        dp[0][0] = 0;

        vector<vector<int>> rmq(MAXBIT, vector<int>(n + 1, INF));
        vector<int> lg(n + 1);
        for (int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;

        auto update = [&](vector<int>& val) {
            for (int i = 0; i <= n; ++i) rmq[0][i] = val[i];
            for (int j = 1; j < MAXBIT; ++j)
                for (int i = 0; i + (1 << j) - 1 <= n; ++i)
                    rmq[j][i] = min(rmq[j - 1][i], rmq[j - 1][i + (1 << (j - 1))]);
        };

        auto query = [&](int l, int r) {
            if (l > r) return INF;
            int len = r - l + 1;
            int b = lg[len];
            return min(rmq[b][l], rmq[b][r - (1 << b) + 1]);
        };

        update(dp[0]);

        auto check = [&](int l, int r, int andV) -> int {
            int res = 0;
            for (int j = 0; j < MAXBIT; ++j)
                if (pre[j][r] - pre[j][l - 1] == r - l + 1)
                    res |= 1 << j;
            // 0 表示整过了
            if ((res & andV) != andV) return 0;
            // 1 表示 res == andV
            if (res == andV) return 1;
            // 2 表示 andV 是 res 的子集
            return 2;
        };

        for (int j = 1; j <= m; ++j) {
            int andV = andValues[j];
            int l = j, r = j;
            for (int i = j; i <= n; ++i) {
                /*
                    优化点在于:
                    我们看暴力的部分:
                        从后往前枚举,什么时候 break ?
                        当 (mask & andV) != andV ,就可以 break 了
                        这部分可以二分,为什么可以二分?
                        因为我们从 i 往前枚举 pi ,这个 mask 在每个二进制位以及整体都是单调下降的

                        二分出 res == andV 的最小的 l
                        继续二分出 res == andV 的最大的 r
                */
                // 暴力解法做法
                // int mask = -1;
                // for (int pi = i; pi >= 1; --pi) {
                //     mask &= nums[pi];
                //     if (mask == andV) dp[j][i] = min(dp[j][i], dp[j - 1][pi - 1] + nums[i]);
                //     if ((mask & andV) != andV) break;
                // }

				// 优化解法1,二分做法
                //int left = j, right = i;
                //while (left < right) {
                //    int mid = (left + right) >> 1;
                //    if (check(mid, i, andV) == 0) left = mid + 1;
                //    else right = mid;
                //}
                //
                //int flag = check(left, i, andV);
                //if (flag != 1) continue;
                //int l = left;
                //
                //right = i;
                //while (left < right) {
                //    int mid = (left + right + 1) >> 1;
                //    if (check(mid, i, andV) == 1) left = mid;
                //    else right = mid - 1;
                //}
                //int r = left;
				
				// 优化解法2双指针做法
                // 首先 l 应该是 == 0 的全部崩掉
                while (l < i && check(l, i, andV) == 0) l += 1;
                if (check(l, i, andV) != 1) continue;

                r = l;
                while (r < i && check(r, i, andV) == 1) r += 1;
                if (check(r, i, andV) == 2) r -= 1;
                dp[j][i] = min(dp[j][i], query(l - 1, r - 1) + nums[i]);
            }

            update(dp[j]);
        }
        return dp[m][n] >= INF ? -1 : dp[m][n];
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/558155.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C#自定义窗体更换皮肤的方法:创建特殊窗体

目录 1.窗体更换皮肤 2.实例 &#xff08;1&#xff09;图片资源管理器Resources.Designer.cs设计 &#xff08;2&#xff09;Form1.Designer.cs设计 &#xff08;3&#xff09;Form1.cs设计 &#xff08;4&#xff09; 生成效果 &#xff08;5&#xff09;一个遗憾 1.窗…

Git常见命令行操作和IDEA图形化界面操作

设置Git用户名和标签 在安装完Git以后需要设置用户和签名&#xff0c;至于为什么要设置用户签名可以看一下这篇文章【学了就忘】Git基础 — 11.配置Git用户签名说明 - 简书 (jianshu.com) 基本语法&#xff1a; git config --global user.name 用户名 git config --global u…

SpringBoot项目创建及简单使用

目录 一.SpringBoot项目 1.1SpringBoot的介绍 1.2SpringBoot优点 二.SpringBoot项目的创建 三.注意点 一.SpringBoot项目 1.1SpringBoot的介绍 Spring是为了简化Java程序而开发的&#xff0c;那么SpringBoot则是为了简化Spring程序的。 Spring 框架&#xff1a; Spring…

ARM之栈与方法

ARM之栈与方法 计算机中的栈是一种线性表&#xff0c;它被限定只能在一端进行插入和删除操作&#xff08;先进后出&#xff09;。通常将可以插入和删除操作的一端称为栈顶&#xff0c;相对的一端为栈底。 通常栈有递增堆栈&#xff08;向高地址方向生长&#xff09;、递减堆栈…

鸿蒙OpenHarmony【搭建Ubuntu环境】

搭建Ubuntu环境 在嵌入式开发中&#xff0c;很多开发者习惯于使用Windows进行代码的编辑&#xff0c;比如使用Windows的Visual Studio Code进行OpenHarmony代码的开发。但当前阶段&#xff0c;大部分的开发板源码还不支持在Windows环境下进行编译&#xff0c;如Hi3861、Hi3516…

Day37 IO流的操作

Day37 IO流的操作 文章目录 Day37 IO流的操作Java的文件拷贝利用 文件字节输出流 向文件写入数据利用 文件字节输入流 读取文件里的数据利用 带缓冲区的字节输出流 向文件写入数据利用 带有缓冲区的字节输入流 读取文件里的数据利用 字符输出转换流 向文件写入数据利用 字符输入…

Java全套智慧校园系统源码springboot+elmentui +Quartz可视化校园管理平台系统源码 建设智慧校园的5大关键技术

Java全套智慧校园系统源码springbootelmentui Quartz可视化校园管理平台系统源码 建设智慧校园的5大关键技术 智慧校园指的是以物联网为基础的智慧化的校园工作、学习和生活一体化环境&#xff0c;这个一体化环境以各种应用服务系统为载体&#xff0c;将教学、科研、管理和校园…

豆瓣影评信息爬取 (爬虫)

代码块&#xff1a; from lxml import etree import requestsheaders{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0 }url_list[] for i in range(0,5):i*20urlsf"https:…

day02-新增员工

day01 新增员工业务逻辑整理 EmployeeController.java PostMappingApiOperation("新增员工")public Result save(RequestBody EmployeeDTO employeeDTO){System.out.println("当前线程的ID:" Thread.currentThread().getId());log.info("新增员工&a…

[leetcode] 56. 合并区间

文章目录 题目描述解题方法排序java代码复杂度分析 题目描述 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区…

UWB人员定位系统适用的场景有哪些?​​​​​​​10厘米工业级实时轨迹高精度定位

UWB人员定位系统适用的场景有哪些&#xff1f;10厘米工业级实时轨迹高精度定位 一、应用场景 1、商场与零售领域&#xff1a;商场可以使用UWB人员定位系统来跟踪顾客的行踪&#xff0c;以收集顾客行为数据&#xff0c;为营销策略提供有力支持。帮助商场优化商品布局和陈列&…

在龙梦迷你电脑福珑2.0上使用Fedora 28 龙梦版

在龙梦迷你电脑福珑2.0上使用Fedora 28 龙梦版。这个版本的操作系统ISO文件是&#xff1a;Fedora28_for_loongson_MATE_Live_7.2.iso 。它在功能方面不错。能放音乐&#xff0c;能看cctv直播&#xff0c;有声音&#xff0c;能录屏&#xff0c;能在局域网里用PuTTY的ssh方式连接…

【Java EE】依赖注入DI详解

文章目录 &#x1f334;什么是依赖注入&#x1f340;依赖注入的三种方法&#x1f338;属性注入(Field Injection)&#x1f338;构造方法注入&#x1f338;Setter注入&#x1f338;三种注入优缺点分析 &#x1f333;Autowired存在的问题&#x1f332;解决Autowired对应多个对象问…

dp思维 枚举

题目链接 #include<bits/stdc.h> using namespace std; #define i64 long long const i64 mod 1e9 7; int main() {int n;cin >> n;vector<char>s(n 1);for (int i 1; i < n; i) {cin >> s[i];}//用ans记录所有满足条件的答案数量&#xff0c;c…

SQL增加主键约束的条件

结论 常见认为设为主键的条件为&#xff1a; 值唯一不含空值 具体实施中会出现各种问题 添加主键约束的条件细则&#xff1a; 值唯一数据中不含空值在定义时需要not null约束&#xff08;使用check约束不行&#xff09; 验证实验 接下来我做了关于这个细则的验证实验&am…

万物皆可计算|下一个风口:近内存计算-2

虽然PIM可以有缓解内存墙的问题&#xff0c;但是PIM设计面临着一系列技术和工程上的挑战&#xff0c;这些挑战直接影响着PIM技术的实用化和广泛应用&#xff1a; 地址翻译与操作映射&#xff1a; 在传统计算机体系结构中&#xff0c;地址空间由操作系统管理和调度&#xff0c;通…

万物皆可计算|下一个风口:近内存计算-1

传统的冯诺依曼架构虽然广泛应用于各类计算系统&#xff0c;但其分离的数据存储与处理单元导致了数据传输瓶颈&#xff0c;特别是在处理内存密集型任务时&#xff0c;CPU或GPU需要频繁地从内存中读取数据进行运算&#xff0c;然后再将结果写回内存&#xff0c;这一过程涉及大量…

Vue3:响应式数据的基本使用(ref、reactive)

一、前言 在Vue3中&#xff0c;如果数据不是响应式数据&#xff0c;当数据的值发生改变时&#xff0c;页面上的数据是不会发生改变的。因此本文主要介绍Vue3中响应式数据的使用&#xff0c;包括ref和reactive的基本使用。 二、ref 1、ref —— 创建基本类型的响应式数据 re…

电大搜题微信公众号:重庆开放大学学子的学习利器

在当今信息化时代&#xff0c;学习已经成为每个人不可或缺的一部分。然而&#xff0c;对于重庆开放大学的学子们来说&#xff0c;由于远程教育的特殊性&#xff0c;他们面临着更大的学习挑战。幸运的是&#xff0c;他们现在可以依靠一款强大的学习利器——电大搜题微信公众号&a…

软考中级网络工程师-2024上岸宝典

1.软考是什么 简单说就是计算机技术 相关的国家级证书考试&#xff0c;想听专业点给大家截一张官网的图&#xff0c;不想听废话直接往下。 同为国家级证书的&#xff1a;注册会计师、法律职业资格证、一级建筑师&#xff0c;证书的价值是比较高的。 很多人都是在求职前或者大…