首页 > 其他分享 >LeetCode 周赛 350(2023/06/18)01 背包变型题

LeetCode 周赛 350(2023/06/18)01 背包变型题

时间:2023-06-19 14:13:13浏览次数:48  
标签:周赛 01 06 val nums Int ret 刷墙 dp

本文已收录到 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)

往期回顾

标签:周赛,01,06,val,nums,Int,ret,刷墙,dp
From: https://www.cnblogs.com/pengxurui/p/17491008.html

相关文章

  • EXPDP导出带LOB字段表报ORA-01555错误解决
    问题概述expdp导出数据,结果遇到如下报错:ProcessingobjecttypeDATABASE_EXPORT/SCHEMA/POST_SCHEMA/PROCACT_SCHEMAORA-31693:Tabledataobject"USER1"."TKINFO"failedtoload/unloadandisbeingskippedduetoerror:ORA-29913:errorinexecutingODCIEX......
  • ABC306G 与 CF1835D 的思考
    两道题似乎都涉及了一个经典模型:在一张有向图上,给定起点\(s\)和终点\(t\),询问\(s\)到\(t\)与\(t\)到\(s\)是否均存在一条长度\(=L\)的路径(\(L\)是一个\(\gen^3\)的数)。首先\(s\)与\(t\)必须在同一个SCC内(考场上没看到互相可达直接以为不可做)。考虑取......
  • 2023-06-19 uniapp云打包报错:app-plus.distribute.icons.android.hdpi 文件不存在
    详细报错:[HBuilder]11:02:51.408Manifest.json文件以下节点配置错误,请检查修复[HBuilder]11:02:51.408app-plus.distribute.icons.android.hdpi 文件不存在[HBuilder]11:02:51.408app-plus.distribute.icons.android.xxhdpi 文件不存在[HBuilder]11:02:51.408ap......
  • 20230406 9.2. 希尔排序( by Donald Shell )
    希尔排序(byDonaldShell)定义增量序列\(D_M>D_{M-1}>…>D_1=1\)对每个\(D_k\)进行\(D_k-间隔\)排序(k=M,M-1,…1)注意:\(D_k-间隔\)有序的序列,在执行\(D_{k-1}-间隔\)排序后,仍然是\(D_k-间隔\)有序的希尔增量序列原始希尔排序$D_M=N/2$......
  • 【2023-06-16】熟悉的地方
    20:00准备是有价值的,也有苦中作乐的美感,你以为在消遣时光,其实你在为未来铺路。                                                 ——张艺谋何太的领导昨天出差了。所......
  • 20230618 java.util.stream.Stream
    介绍java.util.stream.StreampublicinterfaceStream<T>extendsBaseStream<T,Stream<T>>APIstaticbuilder返回Builder创建流:ofofNullableempty创建无限流:iterategenerateconcat<T>Stream<T>concat(Stream<?ext......
  • 01 MyBatis第一个应用程序
    1、MyBatis是什么?mybatis是一个基于java的持久层框架。2、什么是持久化数据由瞬态状态变为持久状态。3、持久层:完成持久化工作的代码块。--DAO层,将数据存到数据库4、MyBatis就是帮助程序员将数据存入数据库中,和从数据库中取数据。5、传统JDBC操作:有很多重复代码块,比如:数......
  • Android - 无法使用任何临时 SqlClient 版本(v2.1.4、v4.1.0、v5Preview)连接到 SQL Ser
    Aconnectionwassuccessfullyestablishedwiththeserver,butthenanerroroccurredduringthepre-loginhandshake.设法用证书和IP地址解决它。使用powershell为您的IP地址创建证书:New-SelfSignedCertificate-certstorelocationcert:\localmachine\my-dns......
  • P2050 [NOI2012] 美食节
    luoguP2050[NOI2012]美食节题意CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴。他很快就尝遍了美食节所有的美食。然而,尝鲜的欲望是难以满足的。尽管所有的菜品都很可口,厨师做菜的速度也很快,小M仍然觉得......
  • Java Websocket 01: 原生模式 Websocket 基础通信
    目录JavaWebsocket01:原生模式Websocket基础通信JavaWebsocket02:原生模式通过Websocket传输文件Websocket原生模式原生模式下服务端通过@ServerEndpoint实现其对应的@OnOpen,@OnClose,@OnMessage,@OnError方法客户端创建WebSocketClient实现对应的......