本文已收录到 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 周赛 350(2023/06/18)01 背包变型题的更多相关文章

  1. hdu 2955 Robberies (01背包好题)

    Robberies Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  2. [Usaco2008 Dec]Hay For Sale 购买干草[01背包水题]

    Description     约翰遭受了重大的损失:蟑螂吃掉了他所有的干草,留下一群饥饿的牛.他乘着容量为C(1≤C≤50000)个单位的马车,去顿因家买一些干草.  顿因有H(1≤H≤5000)包 ...

  3. hihoCoder #1038 : 01背包(板子题)

    #1038 : 01背包 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 且说上一周的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励 ...

  4. POJ 3624 Charm Bracelet(01背包裸题)

    Charm Bracelet Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 38909   Accepted: 16862 ...

  5. HDU 2602 Bone Collector(01背包裸题)

    Bone Collector Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  6. HDU 2602 - Bone Collector - [01背包模板题]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2602 Many years ago , in Teddy’s hometown there was a ...

  7. 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 ...

  8. hdu–2369 Bone Collector II(01背包变形题)

    题意:求解01背包价值的第K优解. 分析: 基本思想是将每个状态都表示成有序队列,将状态转移方程中的max/min转化成有序队列的合并. 首先看01背包求最优解的状态转移方程:\[dp\left[ j ...

  9. HDU 2546 饭卡(01背包裸题)

    饭卡 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submiss ...

  10. P1048 采药(洛谷,动态规划递推,01背包原题)

    题目直接放链接 P1048 采药 这题只是01背包+背景故事而已 原题来的 PS:我写了一篇很详细的01背包说明,如果下面ac代码有看不懂的地方可以去看看 对01背包的分析与理解(图文) 下面上ac代 ...

随机推荐

  1. JQ-DOM与元素的操作

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. CentOS安装时钟同步服务

    使用chrony用于时间同步 yum install chrony -y vim /etc/chrony.conf cat /etc/chrony.conf | grep -v "^#&qu ...

  3. [软件工程]TO B型IT软件企业在工程管理角度所存在的诸多问题

    组织架构与分工? 各子组织的职责.边界是否明确? (安装.升级)部署规范? 必须有部署文档. 各个模块/组件部署在哪台服务器?哪个路径下? 一切非正式启用的任务.文件(夹).安装资料必须依据实际用途以 ...

  4. [网络]内网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 ...

  5. [Java EE]缓存技术初探

    1 背景 使用场景:计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就应当考虑使用缓存. 高并发下,为提高 频繁 查询 大量 可能常用的 数据库数据的 查询效率. 大部分情况下, ...

  6. 4.测试类mapper报错

    1.总结:前几天还有今天一直在弄测试类报错的原因,想着项目是一个大整体,写一个mappe测试类,测试一个mapper,这样后面不会出错: 但是在测试mapper的时候一直,出现mapper值为空的异常 ...

  7. SpringBoot @Target、@Retention、@Documented注解简介

    jdk1.5起开始提供了4个元注解:@Target.@Retention.@Documented.@Inherited.何谓元注解?就是注解的注解. 在程序开发中,有时候我们需要自定义一个注解,这个自 ...

  8. 从0到1手把手教你ASP.NET Core Web API项目配置接口文档Swagger(一)

    一.创建ASP.NET Core Web API项目(若项目已创建,则可跳过本节内容) 1.双击打开VS2022. 2.单击"创建新项目",如下图. 3.选择"ASP.N ...

  9. 实现声明式锁,支持分布式锁自定义锁、SpEL和结合事务

    目录 2.实现 2.1 定义注解 2.2 定义锁接口 2.3 锁的实现 2.3.1 什么是SPI 2.3.2 通过SPI实现锁的多个实现类 2.3.3 通过SPI自定义实现锁 3.定义切面 3.1 切 ...

  10. mysql大表修改工具: pt-online-schame-change

    在表数据量很大的时候直接添加字段,以及其他表结构修改,会严重影响线上使用,而且耗费时间很长:使用这个工具可以很好的在线修改表结构. 好处: 降低主从延时的风险 可以限速.限资源,避免操作时MySQL负 ...