本文已收录到 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. Wikijs简介-强大&可扩展的开源维基软件

    Wikijs - 最强大 最可扩展的开源维基软件 使用 wiki.js 美丽直观的界面,让文档成为写作的乐趣! 优点 随时随地安装 几乎适用于任何平台,并与PostgreSQL.MySQL.Maria ...

  2. 二进制安装Kubernetes(k8s) v1.24.1 IPv4/IPv6双栈

    二进制安装Kubernetes(k8s) v1.24.1 IPv4/IPv6双栈 Kubernetes 开源不易,帮忙点个star,谢谢了 介绍 kubernetes二进制安装 后续尽可能第一时间更新 ...

  3. Zookeeper的深入分析

    运⾏时状态分析 在ZAB协议的设计中,每个进程都有可能处于如下三种状态之⼀ · LOOKING:Leader选举阶段. · FOLLOWING:Follower服务器和Leader服务器保持同步状态. ...

  4. flak_login用法

    基础的信息和全局配置这里就不多说,需要用到再斟酌也可以的.这里也是针对每个模块较为常用的进行解释说明,后期再使用过程中遇到会进行补充. Login_Manager LoginManager是一个类,有 ...

  5. pandas之时间操作

    顾名思义,时间序列(time series),就是由时间构成的序列,它指的是在一定时间内按照时间顺序测量的某个变量的取值序列,比如一天内的温度会随时间而发生变化,或者股票的价格会随着时间不断的波动,这 ...

  6. 四月十五号java基础知识

    1.今天下午做了一个题感受很深,自己做题没有思路或者有点思路死磕也没有搞清楚,看起来很简单的问题,在我手里很难 做咯许久还是室友帮忙解决的,后面重新打一遍还是出问题,找他解决的,问了问他我自己的问题, ...

  7. 10分钟理解React生命周期

    前言 学习React,生命周期很重要,我们了解完生命周期的各个组件,对写高性能组件会有很大的帮助. 一.简介 React /riˈækt/ 组件的生命周期指的是组件从创建到销毁过程中所经历的一系列方法 ...

  8. 扎实打牢数据结构算法根基,从此不怕算法面试系列之004 week01 02-04 使用泛型实现线性查找法

    1.算法描述 在数组中逐个查找元素,即遍历. 2.上一篇文的实现结果 在 扎实打牢数据结构算法根基,从此不怕算法面试系列之003 week01 02-03 代码实现线性查找法中,我们实现了如下代码: ...

  9. If选择语句的用法

    今天我们学习下If判断语句. 首先了解下它有几种用法: If单选择语句 If双选择语句 If多选择语句 我们一个一个用,每一个用法都给一个运用的过程演练一下. If单选择语句:我们很多需要判断一个东西 ...

  10. JavaScript基础语法-变量

    JavaScript JavaScript - 变量 1. 概念 变量是用于存放数据的容器 通过变量名可以获取数据 并且数据是可修改的 2. 使用 声明变量 只声明不赋值 直接调用 程序会输出unde ...