一题多解。
首先理解问题,可以抽象为:

  • 有若干物品,大小分别为quantity[i]
  • 假如某个数字重复x次,可理解为有一个容量为x的背包
  • 要把物品放进背包里,问能不能放完

DFS

暴力解法,直接遍历所有可能,甚至也没有怎么剪枝,竟然也不超时……

class Solution:def canDistribute(self, nums: List[int], quantity: List[int]) -> bool:packages = list(sorted(Counter(nums).values(), reverse=True))quantity = list(sorted(quantity, reverse=True))n, m = len(packages), len(quantity)def distribute(i):if i >= m: return Trueq = quantity[i]for j in range(n):if packages[j] < q: continuepackages[j] -= qif distribute(i+1):return Truepackages[j] += qreturn Falsereturn distribute(0)

记忆化DFS

其实看到这个数据大小大概就知道是个指数级别的复杂度了,那基本上就是记忆化搜索或者状态压缩DP。记忆化递归和DP其实本质上是一样的,一个自顶向下一个自底向上罢了。

不过记忆化搜索比较容易理解。这里需要记录的就是:

  1. 当前放到第几个物品了
  2. 背包容量。因为背包的顺序不重要,所以容量[1,2,1]和容量[1,1,2]其实是一回事,所以索性从大到小排,还方便剪枝。
class Solution:def canDistribute(self, nums: List[int], quantity: List[int]) -> bool:packages = list(sorted(Counter(nums).values(), reverse=True))quantity = list(sorted(quantity, reverse=True))n, m = len(packages), len(quantity)@lru_cache(typed=False)def distribute(i, packs):if i >= m: return Trueq = quantity[i]for j, p in enumerate(packs):if p < q: breaknew_packs = list(packs)new_packs[j] -= qif distribute(i+1, tuple(sorted(new_packs, reverse=True))):return Truereturn Falsereturn distribute(0, tuple(packages))

巨快……

状态压缩DP

状态: 二进制状态,1/0表示第i个物品有/没有被放进去.
状态转移:

  • f[i][s]表示:只用前i个背包,能不能装下s指示的物品。
  • f[i][s]=ORk(f[i−1][k]and weight(s-k)≤capacity[i])f[i][s] = \text{OR}_k(f[i-1][k] \text{ and } \text{weight(s-k)}\le \text{capacity[i]})f[i][s]=ORk​(f[i−1][k] and weight(s-k)≤capacity[i]),OR里的每一项都表示:前一个状态k与当前状态s之间相差的那些物品全放到当前的背包里。
  • 需要预处理每个状态的大小之和weight[s],这样DP的时候不需要再重复计算

注意点:

  • 边界条件:

    • i=0的时候,只有f[i-1][0]为True,其它都是false
    • f[i][0]=True,不管有多少个背包,“不放任何物品”的要求总是可以满足的
    • 前置状态k=s的时候,不要漏掉了(即背包i里一个都不放,全放前面的i-1个背包里了)
  • 快速遍历所有前置状态:利用(k-1) & s!

    x-1的二进制特点:假如x里存在某位为1,则x-1会把最低位的1变成0,最低位的1右边的0全都变成1,而最低位的1左边的所有位都不变。这个特点可以跟其它数字配合在一起做一点位运算,例如:对(x-1)&x来说,x最低位的1左边所有位不变,与之后也不变,而减1之后x最低位的1右边(包括这个1)全都取反了,所以与之后等于0。因此(x-1)&x的结果就是把最低位的1去掉

    (k-1) & s则有这样的特点:对于k最低位的1右边的所有位,如果在s里的对应位为1,那么在(k-1) & s里状态也为1。
    所以可以这样遍历所有要放到背包i里的物品集合selected (= s-k)

    1. 初始:全放,selected = s
    2. 减少一个,selected = (selected - 1) & s,此时k = s - selected
    3. 重复2,直到selected=0

    s = 1001010100为例:

    1. selected = 1001010100 (1111)
    2. selected = 1001010000 (1110)
    3. selected = 1001000100 (1101)
    4. selected = 1001000000 (1100)
    5. selected = 1000010100 (1011)
class Solution:def canDistribute(self, nums: List[int], quantity: List[int]) -> bool:packages = list(Counter(nums).values())n, m = len(packages), len(quantity)M = 1 << m# 预处理每个状态的物品大小之和weights = [0] * Mfor s in range(M):if s == 0: continuefor i in range(m):if s & (1<<i):weights[s] = weights[s - (1<<i)] + quantity[i]f = []for i, p in enumerate(packages):f.append([])for s in range(M):res = False# case 1: f[i][0]# case 2: selected=0if s == 0 or (i > 0 and f[i-1][s]):res = Trueelse:# case 3: selected > 0,背包i里要放东西selected = swhile selected > 0:prev = s - selectedif weights[selected] <= p and (prev==0 or (i>0 and f[i-1][prev])):res = Truebreakselected = (selected-1) & sf[-1].append(res)if f[-1][M-1]:return Truereturn f[-1][M-1]

【LeetCode】1655. Distribute Repeating Integers 分配重复整数相关推荐

  1. LeetCode 1655. 分配重复整数(回溯)

    文章目录 1. 题目 2. 解题 2.1 回溯 1. 题目 给你一个长度为 n 的整数数组 nums ,这个数组中至多有 50 个不同的值. 同时你有 m 个顾客的订单 quantity ,其中,整数 ...

  2. C++找出数组中的第一个非重复整数的算法(附完整源码)

    C++找出数组中的第一个非重复整数的算法 C++找出数组中的第一个非重复整数的算法完整源码(定义,实现,main函数测试) C++找出数组中的第一个非重复整数的算法完整源码(定义,实现,main函数测 ...

  3. LeetCode 每日一题 3. 无重复字符的最长子串

    LeetCode 每日一题 3. 无重复字符的最长子串   大家好,我叫亓官劼(qí guān jié ),在CSDN中记录学习的点滴历程,时光荏苒,未来可期,加油~博客地址为:亓官劼的博客 本文原创 ...

  4. C++ 实现输出 n个不重复整数任取m个数的所有组合(附C语言实现)

    C++ 实现输出 n个不重复整数任取m个数的所有组合(附C语言实现) 一.简要说明 基本实现过程:先得到索引组合,再根据索引打印对应值.附C语言实现版. 二.效果图 三.例子说明 例如 6取4,声明一 ...

  5. LeetCode高频题13:罗马数字转整数

    LeetCode高频题13:罗马数字转整数 提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目 互联网大厂们在公司养了一大批ACM ...

  6. 【LeetCode】1227. 飞机座位分配概率

    一.题目描述 有 n 位乘客即将登机,飞机正好有 n 个座位.第一位乘客的票丢了,他随便选了一个座位坐下. 剩下的乘客将会: 如果他们自己的座位还空着,就坐到自己的座位上, 当他们自己的座位被占用时, ...

  7. leetcode 424. Longest Repeating Character Replacement | 424. 替换后的最长重复字符(Java)

    题目 https://leetcode.com/problems/longest-repeating-character-replacement/ 题解 class Solution {public ...

  8. leetcode 371. Sum of Two Integers | 371. 两整数之和(补码运算)

    题目 https://leetcode.com/problems/sum-of-two-integers/ 题解 根据 related topics 可知,本题考察二进制运算. 第一次提交的时候,没想 ...

  9. [LeetCode] Number of 1 Bits Reverse Integer - 整数问题系列

    目录: 1.Number of 1 Bits  - 计算二进制1的个数 [与运算] 2.Contains Duplicate - 是否存在重复数字 [遍历] 3.Reverse Integer - 翻 ...

最新文章

  1. Leangoo产品白皮书
  2. oracle mysql 创建表,Oracle 创建表用户、空间
  3. 关于测试用例的一些思考
  4. powerdesigner 导出数据库表结构
  5. java测试一个泰勒级数_自己 推导一个 泰勒级数
  6. Web 阶段梳理(2019.1.9)
  7. android基础入门思考题答案,《Android移动应用基础教程》中国铁道出版社课后习题(附答案)...
  8. sklearn.preprocessing.Imputer
  9. html实现“设为首页”加入收藏”代码
  10. EXCEL数据格式,字符型空值““参与计算、数值为0时单元格显示为空(解决了无法使用value()函数将字符型空值““转换为数字型的问题)
  11. 用计算机求算术平方根的按键顺序,6.1 .2 用计算器求算术平方根及算术平方根的应用...
  12. 谁说NTFS不支持UEFI启动的?启动U盘放不了超过4G的文件怎么办?Server2016 Win10 U盘UEFI启动制作方法...
  13. 炫酷的生日快乐网页 【附带源码】
  14. HTML如何实现多个空格
  15. 影响 Redis 性能的 5 大方面的潜在因素
  16. java ssm框架调用微信_Java开发SSM框架微信退款的实现
  17. 卫片图斑_推进土地卫片图斑整治工作
  18. 334. 递增的三元子序列 increasingTriplet
  19. vue-element-admin(花裤衩)运行err
  20. 复数类 class complex

热门文章

  1. android ant批量打包
  2. 【CV系列】照度和最低照度相关概念
  3. Sublime Text 怎么使用打开md,替代Typora
  4. ad stm8l 热电偶_STM8L之ADC
  5. 量化交易 米筐 获取股票列表与历史合约数据
  6. 2款免费的安卓后台游戏
  7. 极大似然估计——简述
  8. Python爬虫实战,QQ音乐爬取全部歌曲
  9. 如何发表高质量的学术论文(硕士、博士均有参考价值)
  10. BugkuCTF中套路满满的题--------never give up