【每日一题Day95】LC1815得到新鲜甜甜圈的最多组数 | 状态压缩dp 记忆化搜索
得到新鲜甜甜圈的最多组数【LC1815】
有一个甜甜圈商店,每批次都烤
batchSize
个甜甜圈。这个店铺有个规则,就是在烤一批新的甜甜圈时,之前 所有 甜甜圈都必须已经全部销售完毕。给你一个整数batchSize
和一个整数数组groups
,数组中的每个整数都代表一批前来购买甜甜圈的顾客,其中groups[i]
表示这一批顾客的人数。每一位顾客都恰好只要一个甜甜圈。当有一批顾客来到商店时,他们所有人都必须在下一批顾客来之前购买完甜甜圈。如果一批顾客中第一位顾客得到的甜甜圈不是上一组剩下的,那么这一组人都会很开心。
你可以随意安排每批顾客到来的顺序。请你返回在此前提下,最多 有多少组人会感到开心。
一天不学习就又开始焦虑了 还是天天学习吧 今天的好难 respect灵神
祝大家兔年大吉钱兔无忧呀
思路
- 首先,我们需要尽可能寻找 x x x组顾客的数量之和为
batchSize
的倍数,那么下一组顾客一定会感到开心,以使开心的顾客总是最多【有点贪心】。 - 比较容易写出的是一组顾客或者两组顾客的数量[两数之和]为
batchSize
的倍数。【计算过程中记录改组客人除以batchSize
的余数,因为我们只关心是否是倍数,不关心数值的大小】 - 由于
batchSize
最大值为9,因此这样计算之后,剩余没有配对的顾客,最多只有4种了,因此我们可以计算三数之和以及四数之和为batchSize
的倍数的组数,如果计算完成后仍有顾客剩余,只有剩余的第一组顾客会满意,将结果+1返回即可【写不出来…】
- 首先,我们需要尽可能寻找 x x x组顾客的数量之和为
思路:假设
batchSize
为 m m m由于我们只关心上一批顾客购买后余下的甜甜圈数量,并且相同几组顾客到达的先后顺序不影响结果,因此存在重复子问题,可以用记忆化搜索最优的顾客顺序对应的分数,并使用状态压缩记录剩下顾客团体的个数以及上一批顾客购买后余下的甜甜圈数量,便于使用位运算进行状态的遍历及转移
记忆化搜索过程
- 当前操作:枚举 [ 0 , n − 1 ] [0,n-1] [0,n−1]中 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
- 当前操作:枚举 [ 0 , n − 1 ] [0,n-1] [0,n−1]中 c n t [ x ] > 0 cnt[x]>0 cnt[x]>0的 x x x,表示一批
状态压缩:使用int类型变量
mask
记录剩下顾客团体的个数以及上一批顾客购买后余下的甜甜圈数量由于剩下的顾客最多有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 n,val
越靠后的,在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位),记为left
:mask<<20
- 从
mask
中取出剩余顾客数量(低20位),记为msk
:mask&((1<<20)-1)
- 从
msk
中取出数量为val[i]
对应的个数:msk>>(i*5)
- 将
left
和msk
组合在一起: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]] > 0res = 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 m为
batchSize
,最坏情况下没有两个一对的,例如 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),主要取决于状态个数
- 时间复杂度: 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 m为
- 由于数字过大,若使用数组会造成空间浪费,因此使用
【每日一题Day95】LC1815得到新鲜甜甜圈的最多组数 | 状态压缩dp 记忆化搜索相关推荐
- 1815. 得到新鲜甜甜圈的最多组数 状态压缩
1815. 得到新鲜甜甜圈的最多组数 有一个甜甜圈商店,每批次都烤 batchSize 个甜甜圈.这个店铺有个规则,就是在烤一批新的甜甜圈时,之前 所有 甜甜圈都必须已经全部销售完毕.给你一个整数 b ...
- 2017广东工业大学程序设计竞赛决赛 题解源码(A,数学解方程,B,贪心博弈,C,递归,D,水,E,贪心,面试题,F,贪心,枚举,LCA,G,dp,记忆化搜索,H,思维题)...
心得: 这比赛真的是不要不要的,pending了一下午,也不知道对错,直接做过去就是了,也没有管太多! Problem A: 两只老虎 Description 来,我们先来放松下,听听儿歌,一起&qu ...
- 【蓝桥杯真题】地宫取宝(搜索-记忆化搜索详解)
链接 [蓝桥杯][2014年第五届真题]地宫取宝 题目描述 X 国王有一个地宫宝库.是 n x m 个格子的矩阵.每个格子放一件宝贝.每个宝贝贴着价值标签. 地宫的入口在左上角,出口在右下角. 小明被 ...
- 巧用记忆化搜索代替暴力递归(洛谷P1464题题解,Java语言描述)
题目要求 P1464题目链接 分析 如果--你信了这题干,真的写了递归--TLE警告!!! 所以,就需要优化嘛-- [−9223372036854775808,9223372036854775807] ...
- 22.11.30打卡 记忆化搜索2水题
Function Run Fun 记忆化搜索模板题, 个人认为比滑雪还简单 需要注意的只有当数组下标为负数的时候需要特判一下 其余直接照抄题目就能过了 /* ⣿⣿⣿⣿⣿⣿⡷⣯⢿⣿⣷⣻⢯⣿⡽⣻⢿⣿⣿⣿⣿ ...
- NOIP 2017 逛公园 记忆化搜索 最短路 好题
题目描述: 策策同学特别喜欢逛公园.公园可以看成一张N个点MM条边构成的有向图,且没有 自环和重边.其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间. ...
- 第23次CSP认证 第4题 收集卡牌(记忆化搜索,状压)
链接:http://118.190.20.162/view.page?gpid=T132 思路:先写一个暴力搜索,然后把中间结点的状态存起来,就是记忆化搜索,状态要存搜索到的层数和选择的卡牌数,0代表 ...
- [置顶] 状态压缩DP 简单入门题 11题
1.每一行用一个二进制数表示, 有些二进制数是题目中不合法的状态,我们可以预处理出一行合法状态的个数,在递推的过程中复杂度就会大大降低. POJ 3254 Corn Fields ...
- 计算机考研408每日一题 day95
欢迎关注点赞收藏评论!
最新文章
- 报错解决:DB::Exception: Scalar subquery returned more than one row.
- 面向对象程序设计上机练习一(函数重载)
- 静态局部变量和全局变量的区别!
- C/C++ 语言的常用开发工具下载
- Memcache 安装与使用
- Zabbix 安装agent
- [0].Net开发者社区--您好大的官威啊!
- linux下安装mysql5.7方法与常见问题
- 7-7 全量复制开销
- take android,Protake
- C#中另类自定义公式计算 字符串转换为计算公式,并得出计算结果【转载】
- python在线游戏_几个简单的python小游戏
- 大数据时代最全的医学公共数据库合集整理
- 更改chm文件的字体大小
- PCM音频格式的深入理解
- 二级题库(C语言)------ 第二套题
- sqlite程序实现
- 功耗大好还是小好_额定功率大好还是小好
- ElasticSearch - 玩转搜索之花式查询
- 训练神经网络的常用方法之共轭梯度法