LeetCode 周赛 350(2023/06/18)01 背包变型题
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 [BaguTree Pro] 知识星球提问。
T1. 总行驶距离(Easy)
- 标签:模拟
T2. 找出分区值(Medium)
- 标签:排序
T3. 特别的排列(Medium)
- 标签:图、状态压缩、回溯
T4. 给墙壁刷油漆(Hard)
- 标签:动态规划、01 背包

T1. 总行驶距离(Easy)
https://leetcode.cn/problems/total-distance-traveled/
题解(模拟)
WA 的举手:
class Solution {
fun distanceTraveled(mainTank: Int, additionalTank: Int): Int {
return mainTank * 10 + Math.min(additionalTank, mainTank / 5) * 10
}
}
这道题需要考虑加油后又补足 5 升油量的情况:
class Solution {
fun distanceTraveled(mainTank: Int, additionalTank: Int): Int {
var ret = 0
var x = mainTank
var y = additionalTank
while (x >= 5) {
val time = x / 5
ret += time * 50
x %= 5
val diff = Math.min(time, y)
y -= diff
x += diff
}
return ret + x * 10
}
}
复杂度分析:
- 时间复杂度:O(log_5{n})
- 空间复杂度:O(1)
T2. 找出分区值(Medium)
https://leetcode.cn/problems/find-the-value-of-the-partition/
题解(排序)
排序后计算最小差值:
class Solution {
fun findValueOfPartition(nums: IntArray): Int {
nums.sort()
var ret = Integer.MAX_VALUE
for(i in 1 until nums.size) {
ret = Math.min(ret, nums[i] - nums[i - 1])
}
return ret
}
}
复杂度分析
- 时间复杂度:O(nlgn)
- 空间复杂度:O(lgn)
T3. 特别的排列(Medium)
https://leetcode.cn/problems/special-permutations/
题解(图 + 状态压缩 + 回溯)
由于题目要求相邻元素之间至少存在单向整除关系,容易想到我们需要预处理数据,记录每个元素在作为 (x, y) 相邻对中的 x 时,下一个数 y 可以选择什么数,即从 x 到 y 存在单向边。
val edge = HashMap<Int, MutableList<Int>>()
for ((i,x) in nums.withIndex()) {
edge[x] = LinkedList<Int>()
for (y in nums) {
if (x == y) continue
if (x % y == 0 || y % x == 0) edge[x]!!.add(y)
}
}
这道题的最大有 14 个数,那么使用全排列将至少需要 14! 种情况,暴力全排列会不会超时呢?可以使用经验值 10! = 3628800 约等于 3 · 10^6,那么 14! 必然大于 3 · 10^6 · 10^4,显然是会超时的。
使用状态压缩可以解决这个问题,我们定义 f(x, s) 表示最后选择 x,且已选择列表为 s 的情况下的方案数,其中 s 中的二进制位表示不同下标的数的选择与未选择状态,通过 s 就可归纳多种排列方案,最后我们使用备忘录来剪枝。由于 14 可以被短整型的位数覆盖,因此我们使用 (1 << 14) - 1 来作为初始状态,使用 0 作为终止条件。
class Solution {
private val MOD = 1000000007
fun specialPerm(nums: IntArray): Int {
val n = nums.size
val mask = 1 shl n
// 预处理
val edge = HashMap<Int, MutableList<Int>>()
for (x in nums.indices) {
edge[x] = LinkedList<Int>()
for (y in nums.indices) {
if (nums[x] != nums[y] && nums[x] % nums[y] == 0 || nums[y] % nums[x] == 0) edge[x]!!.add(y)
}
}
// 备忘录
val memo = Array(n) { IntArray(mask) {-1} }
fun backTrack(preIndex: Int, unUsed:Int) : Int{
// 终止条件
if (unUsed == 0) return 1
// 读备忘录
if (-1 != memo[preIndex][unUsed]) return memo[preIndex][unUsed]
var ret = 0
for (choice in edge[preIndex]!!) {
if (unUsed and (1 shl choice) == 0) continue
ret = (ret + backTrack(choice, unUsed xor (1 shl choice))) % MOD
}
// 存备忘录
memo[preIndex][unUsed] = ret
return ret
}
// 枚举首个元素的多种情况
var ret = 0
for (i in nums.indices) {
ret = (ret + backTrack(i, (mask - 1) xor (1 shl i))) % MOD
}
return ret
}
}
复杂度分析:
- 时间复杂度:O(n2·2n) 总共有 n·2^n 种状态,每种状态的转移次数最多为 O(n);
- 空间复杂度:O(n·2^n) 备忘录空间。
T4. 给墙壁刷油漆(Hard)
https://leetcode.cn/problems/painting-the-walls/
题解(01 背包)
思路参考灵神的题解。
需要考虑到优先让付费油漆匠刷最低开销的墙的贪心方案是错误的。
容易发现对于第 i 面墙来说,当且只有分配给付费油漆匠或免费油漆匠 2 种选择,且有:
- 付费墙数 + 免费墙数 = n
- 付费刷墙时间之和 ≥ 免费墙数
联合两式有:付费墙数 + 付费刷墙时间之和 ≥ n,即 (付费刷墙时间 + 1) 之和 ≥ n。那么,此时问题变成从 n 面墙中选择 x 面付费墙,使得满足 (刷墙时间 + 1) ≥ n 时的最小开销,可以用 0 1 背包模型解决。
我们定义 dp[i][j] 表示考虑到 i 为止,且 (刷墙时间 + 1) 为 j 时的最小开销,则对于 第 i 面墙存在两种转移方式:
- 分配给付费油漆匠(选):那么 dp[i][j] = dp[i - 1][j - time[i] - 1] + cost[i]
- 分配给免费油漆匠(不选):那么 dp[i][j] = dp[i - 1][j]
起始条件:dp[0][0] = 0,表示考虑到第 0 面墙为止,且 (刷墙时间 + 1) 为 0 时的最小开销为 0。
class Solution {
fun paintWalls(cost: IntArray, time: IntArray): Int {
val INF = 0x3F3F3F3F
val n = cost.size
// 刷墙时间超过 n 没有意义
val dp = Array(n + 1) { IntArray(n + 1) { INF } }
// 初始状态(付费刷墙时间为 0,开销为 0)
for (i in 0 .. n) dp[i][0] = 0
// 枚举物品
for (i in 1 .. n) {
// 枚举状态
for (j in 1 .. n) {
val t = time[i - 1] + 1
val c = cost[i - 1]
dp[i][j] = dp[i - 1][j]
dp[i][j] = Math.min(dp[i][j], dp[i - 1][Math.max(j - t, 0)] + c)
}
}
return dp[n][n]
}
}
其中对于 j < t 的情况,由于 j 表示付费刷墙时间之和,而 t 表示刷第 i 面墙的时间。如果 j - t < 0,那么等于刷墙之后丢弃一部分付费刷墙时间,此时的花费不会最坏不会差过从初始状态选第 i 墙的开销,即 dp[i-1][Math.max(j-t,0)] + c。
0 1 背包问题通常可以采用滚动数组优化空间:
class Solution {
fun paintWalls(cost: IntArray, time: IntArray): Int {
val INF = 0x3F3F3F3F
val n = cost.size
// 刷墙时间超过 n 没有意义
val dp = IntArray(n + 1) { INF }
// 初始状态(付费刷墙时间为 0,开销为 0)
dp[0] = 0
// 枚举物品
for (i in 1 .. n) {
// 枚举状态(逆序)
for (j in n downTo 1) {
val t = time[i - 1] + 1
val c = cost[i - 1]
dp[j] = Math.min(dp[j], dp[Math.max(j - t, 0)] + c)
}
}
return dp[n]
}
}
复杂度分析:
- 时间复杂度:O(n^2)
- 空间复杂度:(n)
往期回顾
- LeetCode 单周赛第 348 场 · 数位 DP 模版学会了吗?
- LeetCode 单周赛第 347 场 · 二维空间上的 LIS 最长递增子序列问题
- LeetCode 双周赛第 104 场 · 流水的动态规划,铁打的结构化思考
- LeetCode 双周赛第 103 场 · 区间求和的树状数组经典应用

LeetCode 周赛 350(2023/06/18)01 背包变型题的更多相关文章
- hdu 2955 Robberies (01背包好题)
Robberies Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- [Usaco2008 Dec]Hay For Sale 购买干草[01背包水题]
Description 约翰遭受了重大的损失:蟑螂吃掉了他所有的干草,留下一群饥饿的牛.他乘着容量为C(1≤C≤50000)个单位的马车,去顿因家买一些干草. 顿因有H(1≤H≤5000)包 ...
- hihoCoder #1038 : 01背包(板子题)
#1038 : 01背包 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 且说上一周的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励 ...
- POJ 3624 Charm Bracelet(01背包裸题)
Charm Bracelet Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 38909 Accepted: 16862 ...
- HDU 2602 Bone Collector(01背包裸题)
Bone Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
- HDU 2602 - Bone Collector - [01背包模板题]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2602 Many years ago , in Teddy’s hometown there was a ...
- Jam's balance HDU - 5616 (01背包基础题)
Jim has a balance and N weights. (1≤N≤20) The balance can only tell whether things on different side ...
- hdu–2369 Bone Collector II(01背包变形题)
题意:求解01背包价值的第K优解. 分析: 基本思想是将每个状态都表示成有序队列,将状态转移方程中的max/min转化成有序队列的合并. 首先看01背包求最优解的状态转移方程:\[dp\left[ j ...
- HDU 2546 饭卡(01背包裸题)
饭卡 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...
- P1048 采药(洛谷,动态规划递推,01背包原题)
题目直接放链接 P1048 采药 这题只是01背包+背景故事而已 原题来的 PS:我写了一篇很详细的01背包说明,如果下面ac代码有看不懂的地方可以去看看 对01背包的分析与理解(图文) 下面上ac代 ...
随机推荐
- JQ-DOM与元素的操作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- CentOS安装时钟同步服务
使用chrony用于时间同步 yum install chrony -y vim /etc/chrony.conf cat /etc/chrony.conf | grep -v "^#&qu ...
- [软件工程]TO B型IT软件企业在工程管理角度所存在的诸多问题
组织架构与分工? 各子组织的职责.边界是否明确? (安装.升级)部署规范? 必须有部署文档. 各个模块/组件部署在哪台服务器?哪个路径下? 一切非正式启用的任务.文件(夹).安装资料必须依据实际用途以 ...
- [网络]内网IP的判别与分类
1 内网IP划分 内网IP地址分为A类.B类和C类,其地址范围如下: A类地址: 10.0.0.0 - 10.255.255.255 B类地址: 172.16.0.0 - 172.31.255.255 ...
- [Java EE]缓存技术初探
1 背景 使用场景:计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存. 高并发下,为提高 频繁 查询 大量 可能常用的 数据库数据的 查询效率. 大部分情况下, ...
- 4.测试类mapper报错
1.总结:前几天还有今天一直在弄测试类报错的原因,想着项目是一个大整体,写一个mappe测试类,测试一个mapper,这样后面不会出错: 但是在测试mapper的时候一直,出现mapper值为空的异常 ...
- SpringBoot @Target、@Retention、@Documented注解简介
jdk1.5起开始提供了4个元注解:@Target.@Retention.@Documented.@Inherited.何谓元注解?就是注解的注解. 在程序开发中,有时候我们需要自定义一个注解,这个自 ...
- 从0到1手把手教你ASP.NET Core Web API项目配置接口文档Swagger(一)
一.创建ASP.NET Core Web API项目(若项目已创建,则可跳过本节内容) 1.双击打开VS2022. 2.单击"创建新项目",如下图. 3.选择"ASP.N ...
- 实现声明式锁,支持分布式锁自定义锁、SpEL和结合事务
目录 2.实现 2.1 定义注解 2.2 定义锁接口 2.3 锁的实现 2.3.1 什么是SPI 2.3.2 通过SPI实现锁的多个实现类 2.3.3 通过SPI自定义实现锁 3.定义切面 3.1 切 ...
- mysql大表修改工具: pt-online-schame-change
在表数据量很大的时候直接添加字段,以及其他表结构修改,会严重影响线上使用,而且耗费时间很长:使用这个工具可以很好的在线修改表结构. 好处: 降低主从延时的风险 可以限速.限资源,避免操作时MySQL负 ...