LeetCode 2172. 数组的最大与和【状压DP,记忆化搜索;最小费用最大流】2392

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

给你一个长度为 n 的整数数组 nums 和一个整数 numSlots ,满足2 * numSlots >= n 。总共有 numSlots 个篮子,编号为 1 到 numSlots 。

你需要把所有 n 个整数分到这些篮子中,且每个篮子 至多 有 2 个整数。一种分配方案的 与和 定义为每个数与它所在篮子编号的 按位与运算 结果之和。

  • 比方说,将数字 [1, 3] 放入篮子 1 中,[4, 6] 放入篮子 2 中,这个方案的与和为 (1 AND 1) + (3 AND 1) + (4 AND 2) + (6 AND 2) = 1 + 1 + 0 + 2 = 4 。

请你返回将 nums 中所有数放入 numSlots 个篮子中的最大与和。

示例 1:

输入:nums = [1,2,3,4,5,6], numSlots = 3
输出:9
解释:一个可行的方案是 [1, 4] 放入篮子 1 中,[2, 6] 放入篮子 2 中,[3, 5] 放入篮子 3 中。
最大与和为 (1 AND 1) + (4 AND 1) + (2 AND 2) + (6 AND 2) + (3 AND 3) + (5 AND 3) = 1 + 0 + 2 + 2 + 3 + 1 = 9

示例 2:

输入:nums = [1,3,10,4,7,1], numSlots = 9
输出:24
解释:一个可行的方案是 [1, 1] 放入篮子 1 中,[3] 放入篮子 3 中,[4] 放入篮子 4 中,[7] 放入篮子 7 中,[10] 放入篮子 9 中。
最大与和为 (1 AND 1) + (1 AND 1) + (3 AND 3) + (4 AND 4) + (7 AND 7) + (10 AND 9) = 1 + 1 + 3 + 4 + 7 + 8 = 24 。
注意,篮子 2568 是空的,这是允许的。

提示:

  • n == nums.length
  • 1 <= numSlots <= 9
  • 1 <= n <= 2 * numSlots
  • 1 <= nums[i] <= 15

解法 记忆化搜索/状压DP

数据范围很小,考虑状压DP。

本题与1879. 两个数组最小的异或值之和【记忆化搜索,状压DP,位运算】2145,有些相似但也不同。通过对题目条件的转换,可以缩小二者之间的差别。

由于每个篮子至多可以放 2 2 2 个整数,可以视为 2 n u m S l o t s 2numSlots 2numSlots 个篮子(组成一个数组,该数组中每个元素的值是篮子的原始编号),每个篮子至多可以放 1 1 1 个整数(放了整数,才计算该整数与篮子编号的与值)

由于篮子个数很少,可以用二进制数 x x x 表示这 2 n u m S l o t s 2 numSlots 2numSlots 个篮子中放了数字的篮子集合,其中 x x x 从低到高的第 i i i 位为 1 1 1 ,表示第 i i i 个篮子放了数字、为 0 0 0 表示没有放数字。

m e m o [ i ] [ j ] memo[i][j] memo[i][j] 表示对前 0 ∼ i 0\sim i 0i 个数, j j j 代表的篮子可以被选中时的最大与和。记忆化搜索的代码如下:

class Solution {
public:int maximumANDSum(vector<int>& nums, int numSlots) {// memo[i][j] 表示对前0-i个数,j代表的篮子可以被选中时的最大与和int n = nums.size();vector<vector<int>> memo(n, vector<int>(1 << (numSlots * 2), -1));function<int(int, int)> f = [&](int i, int j) -> int {if (i < 0) return 0;int &ans = memo[i][j];if (ans != -1) return ans;for (int k = 0; k < (numSlots * 2); ++k) {if (j >> k & 1) { // 这个篮子已经被选中ans = max(ans, f(i - 1, j ^ (1 << k)) + (nums[i] & (k / 2 + 1)));}}return ans;};int ans = f(n - 1, (1 << (numSlots * 2)) - 1);return ans;}};

设数字 i i i 的二进制表示中 1 1 1 的个数为 c c c ,定义 f [ i ] f[i] f[i] 表示 n u m s nums nums 的前 c c c 个数字放入篮子数组中的篮子里,且放了数字的篮子集合为 i i i 时的最大与和。初始值 f [ 0 ] = 0 f[0] = 0 f[0]=0

考虑 n u m s [ c ] nums[c] nums[c] 放到一个空篮子中的状态转移方程(下标从 0 0 0 开始,此时 n u m s [ c ] nums[c] nums[c] 还没被放入篮子中),我们可以枚举 i i i 中的 0 0 0 ,即空篮子位置 j j j ,该篮子对应的编号为 j 2 + 1 \dfrac{j}{2} +1 2j+1 ,则有:
f [ i + 2 j ] = max ⁡ ( f [ i + 2 j ] , f [ i ] + ( j 2 + 1 ) & n u m s [ c ] ) f[i + 2^j] = \max( f[i + 2^j],\ f[i] + (\dfrac{j}{2} + 1)\ \&\ nums[c]) f[i+2j]=max(f[i+2j], f[i]+(2j+1) & nums[c])
nums \textit{nums} nums 的长度为 n n n ,最后答案为 max ⁡ c = n ( f ) \max_{c=n}(f) maxc=n(f)

代码实现时需要注意,若 c ≥ n c\ge n cn f [ i ] f[i] f[i] 无法转移,需要跳过。

class Solution {
public:int maximumANDSum(vector<int> &nums, int numSlots) {int ans = 0;vector<int> f(1 << (numSlots * 2));for (int i = 0; i < f.size(); ++i) {int c = __builtin_popcount(i);if (c >= nums.size()) continue;for (int j = 0; j < numSlots * 2; ++j) {if ((i & (1 << j)) == 0) { // 枚举空篮子 jint s = i | (1 << j);f[s] = max(f[s], f[i] + ((j / 2 + 1) & nums[c]));ans = max(ans, f[s]);}}}return ans;}
};

解法2 最小费用最大流

此外还可用最小费用最大流解决,这其实是一种比较典型的建图技巧。

设集合 A A A 为数字,集合 B B B 为篮子,额外建立超级源点和超级汇点:

  • 从源点连容量为 1 1 1 费用为 0 0 0 的边到 A A A 中各点;
  • B B B 中各点连容量为 2 2 2 费用为 0 0 0 的边到汇点;
  • A A A 的每个数字 nums [ i ] \textit{nums}[i] nums[i] B B B 的每个篮子 j j j 连边,容量为 + ∞ +\infty + ,费用为 − nums [ i ] & j -\textit{nums}[i]\& j nums[i]&j ,取负号是为了求最小费用最大流。

这样跑最小费用最大流得到的结果的相反数就是匹配 A A A 中所有数字的最大花费,即最大与和。时间复杂度 O ( n m ( n + m ) ) O(nm(n+m)) O(nm(n+m)) 。Go 的实现:

func maximumANDSum(nums []int, numSlots int) (ans int) {const inf int = 1e9// 集合 A 和 B 的大小n, m := len(nums), numSlots// 建图type neighbor struct{ to, rid, cap, cost int } // 相邻节点、反向边下标、容量、费用g := make([][]neighbor, n+m+2)addEdge := func(from, to, cap, cost int) {g[from] = append(g[from], neighbor{to, len(g[to]), cap, cost})g[to] = append(g[to], neighbor{from, len(g[from]) - 1, 0, -cost})}start := n + m   // 超级源点end := start + 1 // 超级汇点for i, num := range nums {addEdge(start, i, 1, 0)for j := 1; j <= m; j++ {addEdge(i, n+j-1, inf, -(num & j))}}for i := 0; i < m; i++ {addEdge(n+i, end, 2, 0)}// 下面为最小费用最大流模板dist := make([]int, len(g))type vi struct{ v, i int }fa := make([]vi, len(g))spfa := func() bool {for i := range dist {dist[i] = inf}dist[start] = 0inQ := make([]bool, len(g))inQ[start] = trueq := []int{start}for len(q) > 0 {v := q[0]q = q[1:]inQ[v] = falsefor i, e := range g[v] {if e.cap == 0 {continue}w := e.toif newD := dist[v] + e.cost; newD < dist[w] {dist[w] = newDfa[w] = vi{v, i}if !inQ[w] {q = append(q, w)inQ[w] = true}}}}return dist[end] < inf}for spfa() {// 沿 start-end 的最短路尽量增广minFlow := inffor v := end; v != start; {p := fa[v]if c := g[p.v][p.i].cap; c < minFlow {minFlow = c}v = p.v}for v := end; v != start; {p := fa[v]e := &g[p.v][p.i]e.cap -= minFlowg[v][e.rid].cap += minFlowv = p.v}ans -= dist[end] * minFlow}return
}

解法3 3进制状态压缩

……

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Juniper防火墙SSG-140 session 过高问题

1.SSG-140性能参数 2.问题截图 3.解决方法 &#xff08;1&#xff09;通过telnet 或 consol的方法登录到防火墙&#xff1b; &#xff08;2&#xff09;使用get session 查看总的session会话数&#xff0c;如果大于300 一般属于不正常情况 &#xff08;3&#xff09;使用get…

Python的编辑器VScode中文设置和Hello World

Python的编辑器 个人比较常用的用于Python开发的编辑器是VScode&#xff0c;大概的原因应该是免费&#xff0c;且便于项目文件的管理。 VScode中文设置插件及使用方法 VScode下载安装好之后&#xff0c;可以在软件左侧的“扩展”中搜索安装一些插件&#xff0c;用于辅助开发…

H5随机短视频滑动版带打赏源码,可封装APP软件或嵌入式观看

H5随机短视频滑动版带打赏源码&#xff0c;可封装APP软件或嵌入式观看&#xff0c;网站引流必备源码&#xff01; 数据来源抖音和快手官方短视频链接&#xff0c;无任何违规内容&#xff01;可自行添加广告等等&#xff01; 手机端完美支持滑动屏幕观看&#xff08;向上或向右…

思维模型 上瘾模型(hook model)

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。你到底是怎么上瘾&#xff08;游戏/抖音&#xff09;的&#xff1f;我们该如何“积极的上瘾”&#xff1f;让我们来一切揭晓这背后的秘密。 1 上瘾模型的应用 1.1上瘾模型的积极应用 1 学…

RT-Thread学习笔记(三):线程管理

线程管理 线程管理相关概念什么是时间片轮转调度器锁线程运行机制线程的五种状态 动态和静态创建线程区别动态和静态创建线程优缺点RT-Thread动态线程管理函数动态创建线程动态删除线程 RT-Thread静态线程管理函数静态创建线程 线程其他操作线程启动线程延时获得当前执行的线程…

四、基本组件

1. Designer设计师 Designer程序是Qt官方推出的专为设计人员使用的UI设计工具&#xff0c;程序员可以使用此工具大幅降低UI设计的代码量。 Designer设计文件的格式是.ui&#xff0c;需要配合同名的头文件与源文件使用。.ui文件通常被称为界面文件&#xff0c;其内部是xml语法的…

微机原理:汇编语言语句类型与格式

文章目录 壹、语句类型1、语句分类2、常用伪代码和运算符2.1数据定义伪指令2.1.1字节定义伪指令DB&#xff08;8位&#xff09;2.1.2字定义伪指令DW&#xff08;16位&#xff09;2.1.3双字节伪指令DD2.1.4 多字节定义DF/DQ/DT&#xff08;了解&#xff09; 2.2 常用运算符2.2.1…

【数据库】SQL 过滤数据

过滤数据 简单过滤where 子句操作符检查单个值范围值检擦空值检查 高级过滤多个过滤条件求值顺序IN 操作符NOT 操作符 在 s q l sql sql 语句中&#xff0c;通过 WHERE 子句指定搜索条件进行过滤。 简单过滤 包含&#xff1a;WHERE&#xff0c;BETWEEN&#xff0c;IS NULL&a…

面向对象与面向过程讲解

目录 简介 面向过程编程&#xff08;Procedural Programming&#xff09; 什么是面向过程编程&#xff1f; 特点&#xff1a; 面向对象编程&#xff08;Object-Oriented Programming&#xff09; 什么是面向对象编程&#xff1f; 特点&#xff1a; 面向对象 vs. 面向过程…

学习人工智能

在线课程 优达学城 当斯坦福大学讲师 Sebastian Thrun 和 Peter Norvig 将他们的课程“人工智能概论”免费放到网上时&#xff0c;Udacity 开始了在线学习的实验。从那时起&#xff0c;它就受到了巨大的欢迎&#xff08;来自 190 多个国家的 160,000 名学生&#xff09;&#x…

[java进阶]——异常详解,try catch捕获异常,抛出异常

&#x1f308;键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 一、异常的体系结构 二、处理异常的本质 三、异常处理的三种方式 3.1虚拟机jvm处理(默认) 3.2 try catch捕获异常 3.3抛出异常 3.4finally关键字 四、自定义异常 五、总结 一、异常的体系结构 分析&#…

【uniapp】proxy 代理切换至线上测试地址调试接口

本地测试地址形如&#xff1a;http://192.168.124.x:xxxx 线上测试地址形如&#xff1a;https://xxxx.xxxx.com 使用线上地址之后需要修改配置项 secure 为 true const constant require(./src/utils/constant) module.exports {devServer: {proxy: {/api: {target: constan…

Node-EventEmitter的用法

题记 EventEmitter的用法&#xff0c;以下是详细过程和代码。 Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。 Node.js 里面的许多对象都会分发事件&#xff1a;一个 net.Server 对象会在每次有新连接时触发一个事件&#xff0c; 一个 fs.readStream 对象会…

[python-大语言模型]从浅到深一系列学习笔记记录

整体学习路径参照&#xff1a;点这里 python-机器学习-深度学习-大语言模型-数据开发 面向开发者的LLM入门提示原则 面向开发者的LLM入门 学习链接&#xff1a; github地址&#xff1a;https://github.com/datawhalechina/prompt-engineering-for-developers 在线阅读地址&…

【LeetCode】145. 二叉树的后序遍历 [ 左子树 右子树 根结点]

题目链接 文章目录 Python3方法一&#xff1a; 递归 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法二&#xff1a; 迭代 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法三&#xff1a; Morris ⟮ O ( n ) 、 O ( 1 ) ⟯ \lgroup O(n)、O(1) \rgroup ⟮O(n)、O(1)⟯写…

MySQL表操作—存储

建表&#xff1a; mysql> create table sch( -> id int primary key, -> name varchar(50) not null, -> glass varchar(50) not null -> ); Query OK, 0 rows affected (0.01 sec) 插入数据&#xff1a; mysql> insert into sch (id,name,…

c语言练习94:分割链表

分割链表 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 示例 1&#xff1a; 输入&#xff1a;head [1,4,3,2,5,2], x…

短视频矩阵系统源码(搭建)

短视频矩阵源码的开发路径分享如下&#xff1a; 1、首先&#xff0c;确定项目需求和功能&#xff0c;包括用户上传、编辑、播放等。 2、其次&#xff0c;搭建开发环境&#xff0c;选择合适的开发工具和框架。 3、然后&#xff0c;进行项目架构设计和数据库设计&#xff0c;确…

PHPExcel 字母列不够用,针对 AA、AB、AC ... ZZ 这样的列

在PHPExcel 导出功能中&#xff0c;如果字段超过26个字母时&#xff0c;会出现字母不够用A~Z后 AA~AZ来添加后续字段 php中&#xff0c;chr() 函数从指定 ASCII 值返回字符&#xff0c;可以自定义一个方法来返回对应的字母 // $num 列数 1,2,3,4,5,6,7...... function getCol…

弹出框,使用树结构查询数据

效果如下: 描述:希望点击某个按钮,弹出一个窗口,然后通过下拉框,点击下拉框里面的组织信息,然后查询对应组织的成员对象列表,并展示到表格中 HTML代码(最主要的就是树的那个): <el-dialog :visible.sync="TesteePage.showDialog" width="70%&quo…