【每日一题Day95】LC1815得到新鲜甜甜圈的最多组数 | 状态压缩dp 记忆化搜索

news2024/9/23 19:17:16

得到新鲜甜甜圈的最多组数【LC1815】

有一个甜甜圈商店,每批次都烤 batchSize 个甜甜圈。这个店铺有个规则,就是在烤一批新的甜甜圈时,之前 所有 甜甜圈都必须已经全部销售完毕。给你一个整数 batchSize 和一个整数数组 groups ,数组中的每个整数都代表一批前来购买甜甜圈的顾客,其中 groups[i] 表示这一批顾客的人数。每一位顾客都恰好只要一个甜甜圈。

当有一批顾客来到商店时,他们所有人都必须在下一批顾客来之前购买完甜甜圈。如果一批顾客中第一位顾客得到的甜甜圈不是上一组剩下的,那么这一组人都会很开心。

你可以随意安排每批顾客到来的顺序。请你返回在此前提下,最多 有多少组人会感到开心。

一天不学习就又开始焦虑了 还是天天学习吧 今天的好难 respect灵神
祝大家兔年大吉钱兔无忧呀

  • 思路

    • 首先,我们需要尽可能寻找 x x x组顾客的数量之和为batchSize的倍数,那么下一组顾客一定会感到开心,以使开心的顾客总是最多【有点贪心】。
    • 比较容易写出的是一组顾客或者两组顾客的数量[两数之和]为batchSize的倍数。【计算过程中记录改组客人除以batchSize的余数,因为我们只关心是否是倍数,不关心数值的大小】
    • 由于batchSize最大值为9,因此这样计算之后,剩余没有配对的顾客,最多只有4种了,因此我们可以计算三数之和以及四数之和为batchSize的倍数的组数,如果计算完成后仍有顾客剩余,只有剩余的第一组顾客会满意,将结果+1返回即可【写不出来…】
  • 思路:假设batchSize m m m

    由于我们只关心上一批顾客购买后余下的甜甜圈数量,并且相同几组顾客到达的先后顺序不影响结果,因此存在重复子问题,可以用记忆化搜索最优的顾客顺序对应的分数,并使用状态压缩记录剩下顾客团体的个数以及上一批顾客购买后余下的甜甜圈数量,便于使用位运算进行状态的遍历及转移

    • 记忆化搜索过程

      • 当前操作:枚举 [ 0 , n − 1 ] [0,n-1] [0,n1] c n t [ x ] > 0 cnt[x]>0 cnt[x]>0 x x x,表示一批group[i] mod m = x 的顾客前来购买甜甜圈
      • 子问题、哪些操作会影响数据:余下的甜甜圈数量 l e f t left left,以及剩余可以选的元素个数 c n t [ x ] cnt[x] cnt[x]【dfs函数的两个参数->使用状态压缩至一个int类型变量中】
      • 下一个子问题:那么 l e f t left left需要修改为$(left + x)%m ,并将 ,并将 ,并将cnt[x]$减1。如果left=0时,下一批顾客会开心,结果加1
    • 状态压缩:使用int类型变量mask记录剩下顾客团体的个数以及上一批顾客购买后余下的甜甜圈数量

      image-20230122163148097

      • 由于剩下的顾客最多有4种,而 g r u o p s [ x ] ≤ 30 gruops[x] \le 30 gruops[x]30,因此可以用5个比特表示顾客 x x x的个数,一共需要20个比特,并使用 l e f t left left记录上一批顾客购买后剩下的甜甜圈数量, l e f t < 9 left \lt 9 left<9,因此需要4个比特,因此可以使用int类型变量mask记录剩下顾客团体的个数以及上一批顾客购买后余下的甜甜圈数量

      • 使用int数组记录每个顾客团体对应的顾客数量val[i],数组长度为 n n nval 越靠后的,在 mask 中的比特位越高

        比如val[0]对应mask中第0位至第4位,val[0]对应mask中第5位至第9位

    • 状态转移过程
      $$
      score=\left{
      \begin{aligned}
      1\qquad left=0 \
      0\qquad left !=0 \

      \end{aligned}
      \right.
      \
      dp[mask] = max {dp[mask],score + dfs[nextMask])}
      $$

  • 实现

    首先遍历数组,记录单组顾客以及两组是 m m m倍数的个数ans,然后使用状态压缩定义mask,记忆化搜索每个可能的状态对应的分数,并记录至map集合中,定义 d f s ( m a s k ) dfs(mask) dfs(mask) 表示剩余顾客数量和left数量为mask时,往下进行操作能获得的最大分数,那么 a n s + = d f s ( m a s k ) ans+ =dfs(mask) ans+=dfs(mask) 即为最终结果

    • 由于数字过大,若使用数组会造成空间浪费,因此使用map集合,存储状态对应的分数
    • 位运算操作
      • mask中取出(高4位),记为leftmask<<20
      • mask中取出剩余顾客数量(低20位),记为mskmask&((1<<20)-1)
      • msk中取出数量为val[i]对应的个数:msk>>(i*5)
      • leftmsk组合在一起:left<<20|mask-(1<<j)
    class Solution {
        private int m;
        private int[] val;
        private final Map<Integer, Integer> cache = new HashMap<>();
    
        public int maxHappyGroups(int batchSize, int[] groups) {
            m = batchSize;
            int ans = 0;
            int[] cnt = new int[m];
            // 秒啊 一次遍历解决
            for (int x : groups) {
                x %= m;
                if (x == 0) ++ans; // 直接排在最前面
                else if (cnt[m - x] > 0) {
                    --cnt[m - x]; // 配对:两组顾客的和为batchSize的倍数
                    ++ans;
                } else ++cnt[x];
            }
    		// 剩下的顾客最多有4种 统计并记录剩余顾客种类以及对应组数
            int mask = 0, n = 0;
            for (int c : cnt) if (c > 0) ++n;
            val = new int[n];// 从大到小
            for (int x = 1; x < m; ++x)
                if (cnt[x] > 0) {
                    val[--n] = x; // val 越靠后的,在 mask 中的比特位越高
                    mask = mask << 5 | cnt[x];
                }
    
            return ans + dfs(mask);
        }
    
        private int dfs(int mask) {
            if (cache.containsKey(mask)) return cache.get(mask);
            int res = 0, left = mask >> 20, msk = mask & ((1 << 20) - 1);
            for (int i = 0, j = 0; i < val.length; ++i, j += 5) // 枚举顾客
                if ((msk >> j & 31) > 0) // cnt[val[i]] > 0
                    res = Math.max(res, (left == 0 ? 1 : 0) + dfs((left + val[i]) % m << 20 | msk - (1 << j)));
            cache.put(mask, res);
            return res;
        }
    }
    
    作者:灵茶山艾府
    链接:https://leetcode.cn/problems/maximum-number-of-groups-getting-fresh-donuts/solutions/2072545/by-endlesscheng-r5ve/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 复杂度
      • 时间复杂度: O ( m 2 ( n m ) m / 2 ) O(m^2(\frac{n}{m})^{m/2}) O(m2(mn)m/2) n n n为数组的长度, m m mbatchSize,最坏情况下没有两个一对的,例如 groups 中只有 [1,4] 中的数。根据基本不等式,把 30 平分成 8+8+7+7 是最优的,那么至多有 9×(8×8×7×7)=28224 个状态,每个状态执行至多 4次循环,因此记忆化搜索的计算量至多为 28224×4=112896
      • 空间复杂度: O ( m ( n m ) m / 2 ) O(m(\frac{n}{m})^{m/2}) O(m(mn)m/2),主要取决于状态个数

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

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

相关文章

LeetCode刷题模版:187-189、198-200

目录 简介187. 重复的DNA序列188. 买卖股票的最佳时机 IV【未理解】189. 轮转数组198. 打家劫舍199. 二叉树的右视图200. 岛屿数量结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简…

【论文翻译】Attention Is All You Need

【论文】Attention is All you Need (neurips.cc) 摘要 主要的序列转换模型是基于复杂的循环或卷积神经网络&#xff0c;其中包括一个编码器和一个解码器。表现最好的模型还通过注意机制连接编码器和解码器。我们提出了一个新的简单的网络架构&#xff0c;Transformer&#xf…

线程常用方法及常见状态

终止线程应该怎么终止一个线程呢&#xff1f;当线程完成任务时。通过使用变量来控制run方法退出的方式停止线程&#xff0c;即通知方式。这里详细介绍一下2的方式。在A线程依靠变量循环跑的过程ing&#xff0c;主线程通过修改A线程的变量&#xff0c;来控制线程终止。为A线程中…

SpringBoot配置文件详解

简介 SpringBoot全局配置文件默认为src/main/resources下的application.properties&#xff0c;后缀可以改为yml&#xff0c; 如果application.yml和application.properties两个配置文件都存在&#xff0c;那么&#xff0c;properties优先级更高 官网(Spring Boot 全部配置项)&…

01_kobject和ktype创建设备文件和设备目录

总结:创建设备文件的方法 设备文件属性指的是 /sys/yyy/xxx yyy:代表这个设备的目录 xxx:代表这个驱动设备的各种属性,我们可以直接操控属性来控制这个设备 比如之前常见的 echo 5 > /sys/led/brightness 直接操作这个属性来更改led的亮度 1 创建设备kobj对象,绑定目录 k…

C语言中不定参数 ... 的语法、函数封装

文章目录Intro语法测试依赖库新函数使用测试&#xff1a;遍历并打印不定参数中的值用两种方式封装函数&#xff1a;对多个int值求和总结Intro 有一天看C代码看到某个方法有这样的定义&#xff1a;在函数形参列表处&#xff0c;有...的写法&#xff0c;就像Java中的不定参数那样…

JDK8 新特性之Stream流方法详解

目录 一&#xff1a;集合处理数据的弊端 二&#xff1a;Stream流式思想概述 小结 &#xff1a; 三&#xff1a;获取Stream流的两种方式 方式1 : 根据Collection获取流 方式2 : Stream中的静态方法of获取流 小结 四&#xff1a;Stream常用方法和注意事项 Stream常用方法…

19. 函数基础知识详解

1. 什么是函数 函数是组织好的&#xff0c;可重复使用的&#xff0c;用来实现单一&#xff0c;或相关联功能的代码段。函数能提高应用的模块性&#xff0c;和代码的重复利用率。之前文章中我们已经使用过python提供的内建函数&#xff0c;比如print()。但你也可以自己创建函数…

【JUC并发编程】线程池及相关面试题 详解

【JUC并发编程】线程池及相关面试题 详解 参考资料&#xff1a; 第十二章 线程池原理 深入浅出Java多线程原理 两道面试题&#xff0c;深入线程池&#xff0c;连环17问 深入理解Java并发编程之线程池、工作原理、复用原理及源码分析 硬核干货&#xff1a;4W字从源码上分析JUC…

Java二叉树OJ题

目录1. 检查两颗树是否相同2. 另一颗树的子树3. 翻转二叉树4. 判断一颗二叉树是否是平衡二叉树4.1 时间复杂度为O(n*n)【从上而下递归】4.2 时间复杂度为O(n)【从下而上递归】5. 对称二叉树6. 二叉树的构建及遍历7. 二叉树创建字符串8. 两个指定节点的最近公共祖先8.1 指定结点…

java入门作业-DAO,读取sql数据库

DAO意思是数据库、访问、对象。有了前后端思想。 需要下载mysql&#xff0c;并下载链接自己设置好密码。可以在navicat等软件链接打开。 材料是jc0122.sql&#xff0c;里面的admin_info是要操作的表。不像上一节需要把数据库文件放到java目录里。数据库只要在本地即可。 一、…

浅析Spring的五大类注解和方法注解

简单的将bean对象存储到Spring容器中&#xff0c;可以使用五大类注解实现&#xff0c;也可以通过Bean方法注解实现。本文重点围绕这几个问题展开&#xff1a;1.为什么需要五大类注解&#xff1f;2.五大类注解之间有没有关系&#xff1f;3.Spring使用五大类注解生成beanName问题…

Linux常见命令 17 - 帮助命令 man,whatis,--help,info,help

目录 1. 查看命令/配置文件帮助信息 man 2. 查看简短的命令帮助信息 whatis 3. 查看简短的命令选项信息 --help 4. 另一查看命令文件帮助信息 info 5. Shell内置帮助命令 help 1. 查看命令/配置文件帮助信息 man [1] 语法&#xff1a;man [命令] 示例&#xff1a;如果想要查…

分享129个ASP源码,总有一款适合您

ASP源码 分享129个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 129个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/1dUPOSf1BudsK-bB4FnGXfQ?pwdg3ae 提取码&#x…

黑马2021-8Vue自学笔记hm

黑马2021-8Vue教程学习笔记 文章目录黑马2021-8Vue教程学习笔记代码和笔记不断更新gitee代码仓库地址备用前端gei忽略提交文件webpack的使用初始化包管理配置文件 package.json下载webpack解决问题dependencies 和 devDependencies区别:在项目中配置webpackWebpack 中的默认约定…

python基础——列表推导式

python基础——列表推导式 文章目录python基础——列表推导式一、实验目的二、实验原理三、实验环境四、实验内容五、实验步骤一、实验目的 掌握Python数据结构&#xff1a;列表推导式的用法。 二、实验原理 列表推导式&#xff08;list comprehension&#xff09;列表推导式…

【Python百日进阶-Web开发-Linux】Day235 - Win11的WSL2中安装Docker

文章目录一、Docker是什么二、Win11安装Docker2.1 官网下载2.2 系统要求&#x1f517;2.2.1 WSL 2 后端2.2.2 Hyper-V 后端和 Windows 容器2.2.3 关于 Windows 容器2.3 在 Windows 上安装 Docker Desktop2.3.1 交互式安装2.3.2 从命令行安装2.4 启动 Docker 桌面三、WSL2的Ubun…

ConcurrentHashMap的死循环问题

文章目录前言1. 情景复现2. 源码解析3. 代码调试4. 原因5. 解决前言 对于ConcurrentHashMap来说&#xff0c;能保证多线程下的安全问题&#xff0c;但是在JDK1.8中还是会出现一个bug&#xff0c;就是computeIfAbsent&#xff0c;下面就来详细说说死循环的原因 1. 情景复现 首…

进阶C语言 第二章-------《进阶指针》 (指针数组、数组指针、函数指针、回调指针)知识点+基本练习题+深入细节+通俗易懂+完整思维导图+建议收藏

绪论 书接上回&#xff0c;通过对数据类型进阶的认识&#xff0c;你肯定对各种数据类型在内存中如何存储有了了解。虽然说&#xff0c;这方面可能对你的编程能力没什么进步。但是&#xff0c;他是一本内功秘籍&#xff0c;当我们遇到了这方面的问题时我们可以知道可能是哪一方面…

使用 Grafana 请求API接口

目的: 使用Grafana 配合JSON API 插件 请求API接口,完成可视化,实现一些简单的请求功能 假设我们想将如下的API接口返回的json数据可视化 这里借用一下 小熊同学的 金融数据接口 用请求如下接口举例 https://api.doctorxiong.club/v1/fund/detail?code000001&startDat…