day43|动态规划5-不同0-1背包的问题形态

文章介绍了几个基于背包问题的动态规划算法,包括经典的0-1背包、分割等和子集、最后一块石头的重量II、目标和以及一和零问题。通过将问题转化为装填背包以达到特定目标的优化问题,利用动态规划的dp数组和递推公式求解最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关键点: 找到前后两种状态的依赖关系

  1. 经典0-1背包: 给定一个背包,问装满该背包的最大价值。
  2. 分割等和子集: 给定一个背包,能不能装满该背包(将重量抽象成价值)
  3. 最后一块石头重量: 给一个一定容量的背包,最多能装多少
  4. 目标和: 装满背包有多少种方法
  5. 一和零 :背包成为了多条件的情况

1049. 最后一块石头的重量 II

分割等和子集 的问题比较像

思考过程: 要想碰撞得到的剩下石头比较小,那么需要将石头尽可能分成相等的两堆。
转化成0-1背包: 将石头的重量抽象成价值,将一半石头的重量抽象成背包的最大容量。

  1. dp[j]:装满容量为j的物品的最大价值
  2. dp数组初始化:dp[0]=1
  3. 递推公式:dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
  4. 遍历顺序:第一层遍历物品,第二层遍历背包注意需要倒序进行遍历。
  5. 打印dp数组
class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        target = sum(stones)//2
        dp = [0]*(target+1)
        dp[0] = 0
        for i in range(len(stones)):
            curValue = stones[i]
            curWeight = stones[i]
            for j in range(target,curWeight-1,-1):
                if curWeight > j:
                    break
                else:
                    dp[j] = max(dp[j],dp[j-curWeight]+curValue)
        leftPile = dp[-1]
        rightPile = sum(stones)-leftPile
        return abs(leftPile - rightPile)

494. 目标和

将原始的问题进行分割,摒弃最直接的遍历+和-,而是把它当成是分成两个组合。
给一个背包的容量,问有多少种方法装满这个容量的背包。找到left的公式,将其装化成如何装满容量大小为left的背包有多少种方案。

  1. dp[i]:表示装满容量为i的背包的方法一共有多少种方式
  2. 递推公式:dp[i] += dp[i-nums[j]]
  3. 遍历顺序
  4. 初始化:看数组中0的个数
  5. 打印dp数组
    在这里插入图片描述
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        Sum = sum(nums)
        left = (target + Sum)//2
        # 如果最后的结果和现有的数值相差太大,也不太行
        if abs(target) > Sum or (target + Sum) % 2 == 1:
            return 0
        dp = [0] * (left + 1)
        dp[0] = 1
        for i in range(len(nums)):
            for j in range(left,nums[i]-1,-1):
                dp[j] += dp[j-nums[i]]
        return dp[left]

474.一和零

在这里插入图片描述
思考: 可以把m个0,n个1当前一个容器,找到装满该容器最多有少个物品,最后输出这个物品即可。
每个物品只能使用一次,只不过现在每个背包有两个维度,可以理解成重量A和重量B

  1. dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]
  2. 确定递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
  3. 遍历顺序:因为在确定背包的时候有两个维度,所以需要有两层循环来遍历背包的情况。
  4. dp数组初始化:
  5. 打印dp数组
class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        dp = [[0] * (n + 1) for _ in range(m + 1)]	# 默认初始化0
        # 遍历物品,每一个物品的重量是什么样的记录每一个物品的重量,然后根据物品的重量继续寻找下边背包的条件。
        for str in strs:
            ones = str.count('1')
            zeros = str.count('0')
            # 遍历背包容量且从后向前遍历!
            for i in range(m, zeros - 1, -1):
                for j in range(n, ones - 1, -1):
                    dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1)
        return dp[m][n]

<think>嗯,这个问题是要找出温度比前一天高的所有日期的id。首先,我需要仔细看题目要求。表格结构是id、recordDate和temperature,其中id是唯一的,recordDate也不会有重复的。那么,我需要比较每一行记录的温度和它前一天的记录的温度,如果当天的温度更高,就选出这个id。 那问题是如何找到每条记录对应的前一天的数据呢?因为日期可能不是连续的,所以不能简单地按id的顺序来比较。比如,假设存在2015-01-01,之后是2015-01-03,那么中间缺少一天,这时候2015-01-03的前一天应该是2015-01-02,但可能表中没有这条记录,所以这时候这条记录的前一天不存在,不需要比较。 所以,正确的做法应该是将每条记录与它的前一天的记录连接起来。这时候可能需要用到自连接,或者窗口函数来获取前一天的temperature。 首先考虑自连接的方式。自连接的条件是,表a的recordDate等于表b的recordDate加一天。这样,表a中的每条记录会和表b中的前一天记录连接。然后比较两者的温度,如果a的温度大于b的,就选出a的id。 比如,示例中的输入: id=2的记录日期是2015-01-02,对应的前一天是2015-01-01,温度是10。此时25>10,所以id=2被选中。而id=4的日期是2015-01-04,前一天是2015-01-03,温度20,30>20,所以选中。 那么自连接的SQL应该怎么写呢? 假设表名是Weather,我们可以用如下方式: SELECT a.id FROM Weather a JOIN Weather b ON a.recordDate = DATE_ADD(b.recordDate, INTERVAL 1 DAY) WHERE a.temperature > b.temperature; 这样,a是当天的记录,b是前一天的记录。连接条件是a的日期等于b的日期加一天。然后筛选出温度更高的a的id。 这个方法是否能处理日期不连续的情况呢?比如,假设存在一个日期是2015-01-05,而表中没有2015-01-04的记录,那么这个记录不会被连接到任何b的记录,所以不会被选中。这样是正确的,因为它没有前一天的数据可以比较。 那这种方法应该可以解决问题。此外,需要注意recordDate的索引是否会影响性能,但题目中没有提到性能问题,所以可能不需要考虑。 那这个查询的返回结果是否正确呢?比如示例中的情况,用上面的查询会得到id 2和4,符合预期。 那是否有其他方法?比如使用LAG窗口函数。LAG函数可以获取前一行的值,但这里的问题是按日期顺序的前一行可能不是前一天。例如,原表中的记录可能不是按日期连续排列的,所以窗口函数需要按日期排序,然后取前一行,然后检查前一行是否是前一天的日期。 比如,假设使用LAG函数: SELECT id FROM ( SELECT id, temperature, LAG(temperature) OVER (ORDER BY recordDate) as prev_temp, LAG(recordDate) OVER (ORDER BY recordDate) as prev_date FROM Weather ) t WHERE temperature > prev_temp AND DATEDIFF(recordDate, prev_date) = 1; 这样,先按日期排序,然后取出前一天的temp和日期,然后比较温度,并确保日期差是1天。这样也能得到正确的结果。 那么这两种方法哪种更好? 自连接的方式可能需要处理更多的数据,因为如果有很多记录,自连接可能会产生较多的组合。而窗口函数的方式则只需要遍历一次数据,然后在子查询中进行筛选。不过,具体性能可能取决于数据库的优化情况。 在本题中,两种方法都可以解决问题。根据不同的数据库版本,可能需要选择不同的方法。比如,如果数据库不支持窗口函数(如旧版本的MySQL),则必须使用自连接的方式。而如果支持窗口函数的话,可能窗口函数的方式更高效,尤其是在数据量大的情况下,因为自连接可能需要更多的比较。 现在,回到问题中,编写解决方案。假设用户使用的是支持窗口函数的MySQL版本,那么两种方法都可以。但需要确保使用的是正确的语法。 比如,对于自连接的方式,需要注意日期的加减操作。在MySQL中,DATE_ADD函数可以用来添加一天。例如,DATE_ADD(b.recordDate, INTERVAL 1 DAY)等于a.recordDate。这可能更直接。 而使用DATEDIFF函数的话,DATEDIFF(a.recordDate, b.recordDate) = 1,这可能更直观。或者,可以将连接条件改为b.recordDate = a.recordDate - INTERVAL 1 DAY。比如: JOIN Weather b ON b.recordDate = a.recordDate - INTERVAL 1 DAY 这可能在MySQL中是可行的。 那现在需要确保语法正确。比如,在MySQL中,日期可以直接进行加减操作。例如,a.recordDate - INTERVAL 1 DAY等于前一天的日期。所以,连接条件可以是: ON b.recordDate = a.recordDate - INTERVAL 1 DAY 这样更直接,可能效率也更高,因为不需要使用DATE_ADD函数。 那自连接的SQL可以写成: SELECT a.id FROM Weather a INNER JOIN Weather b ON b.recordDate = DATE_SUB(a.recordDate, INTERVAL 1 DAY) WHERE a.temperature > b.temperature; 或者: SELECT a.id FROM Weather a, Weather b WHERE b.recordDate = DATE_SUB(a.recordDate, INTERVAL 1 DAY) AND a.temperature > b.temperature; 这样也是可以的。这两种写法是否等价?是的,都是内连接的方式。 这样是否能正确处理示例数据?比如,对于id=2的日期是2015-01-02,那么DATE_SUB得到的是2015-01-01,对应的b的recordDate是这个日期,所以找到对应的b的记录,比较温度。 是的,这样可以得到正确结果。 那这个方法应该没问题。而窗口函数的方法需要考虑,在子查询中的prev_date是否正好是当前日期的前一天。比如,可能存在这样的情况:在按日期排序后,前一条记录不是前一天的情况。比如,假设记录是1号、3号、4号。那么对于4号的记录,前一条是3号,日期差是1天,所以符合条件。而对于3号的记录,前一条是1号,日期差是2天,不会被选中。 所以,使用窗口函数的方法也是可行的,但需要同时比较温度差和日期差是否为1天。 现在,哪种方法更好?可能自连接的方式更直观,但窗口函数的方式可能更高效,尤其是在数据量大的情况下,因为自连接可能需要扫描更多的数据。 不过,在本题的情况下,两种方法都可以解决问题。因此,可以任选其一。考虑到用户可能使用的数据库版本,如果支持窗口函数,那么两种方法都可以。否则,必须使用自连接的方式。 那么,根据题目示例,应该返回正确的id。 综上,正确的解决方案应该使用自连接或者窗口函数的方式。现在写出具体的SQL语句。 例如,使用自连接的方式: SELECT a.id FROM Weather a JOIN Weather b ON a.recordDate = DATE_ADD(b.recordDate, INTERVAL 1 DAY) WHERE a.temperature > b.temperature; 或者: SELECT a.id FROM Weather a JOIN Weather b ON b.recordDate = DATE_SUB(a.recordDate, INTERVAL 1 DAY) WHERE a.temperature > b.temperature; 这两种写法都是正确的,取决于如何表达日期关系。 而使用窗口函数的写法: SELECT id FROM ( SELECT id, recordDate, temperature, LAG(temperature) OVER (ORDER BY recordDate) as prev_temp, LAG(recordDate) OVER (ORDER BY recordDate) as prev_date FROM Weather ) AS temp_table WHERE temperature > prev_temp AND DATEDIFF(recordDate, prev_date) = 1; 这个子查询中的DATEDIFF检查是否是恰好相隔一天。 两种方法都应该得到正确的结果。 现在测试示例数据: 示例输入中的Weather表数据: id | recordDate | Temperature 1 | 2015-01-01 | 10 2 | 2015-01-02 |25 3 | 2015-01-03 |20 4 |2015-01-04 |30 对于自连接的方法: 当a是id=2的记录时,它的recordDate是2015-01-02。连接条件是找到b的recordDate等于2015-01-02 -1天,即2015-01-01。对应的b是id=1的记录,温度10。此时25>10,所以选中id=2。 当a是id=4时,recordDate是2015-01-04,b的recordDate是2015-01-03,温度20。30>20,所以选中id=4。 其他记录,比如id=3,它的recordDate是2015-01-03,连接到的b是2015-01-02的温度25。此时20<25,不会被选中。所以结果正确。 对于窗口函数的方法: 每个记录的prev_date和prev_temp是前一条记录的日期和温度。例如: id=2的prev_date是2015-01-01,温度10。日期差是1,温度25>10,所以选中。 id=4的prev_date是2015-01-03,温度20。日期差1,温度30>20,选中。 id=3的prev_date是2015-01-02,温度25。此时温度20<25,所以不选。 id=1没有prev_date,所以不选。这样结果正确。 因此,两种方法都正确。 所以,最终可以选择其中一种方式写出SQL。比如,自连接方式比较直接,可能在MySQL中更常用。 现在,编写解决方案的步骤: 步骤一:自连接当前表,找到每条记录对应的前一天的记录。 步骤二:比较温度,筛选出当天温度更高的记录。 步骤三:返回这些记录的id。 所以,最终的SQL语句应该是: SELECT a.id FROM Weather a JOIN Weather b ON DATEDIFF(a.recordDate, b.recordDate) = 1 WHERE a.temperature > b.temperature; 或者使用DATEDIFF函数来判断日期差是否为1。这可能更直观,因为无需处理日期的加减,直接判断差值为1。比如,在MySQL中,DATEDIFF(a, b)返回a - b的天数差。所以,当a.recordDate比b.recordDate晚1天时,差值为1。这样,条件写成DATEDIFF(a.recordDate, b.recordDate) = 1,然后连接条件。 这可能更简洁,但需要注意是否会导致笛卡尔积过大。不过,由于每个a最多只能有一个对应的b,或者没有,所以内连接应该正确。 例如,假设有一个a的记录,它的recordDate是某个日期,而存在一个b的日期比它早一天。这时候DATEDIFF的结果是1,所以连接条件成立。如果没有这样的b记录,就不会被选中。 所以,这样的写法同样有效。例如,上述示例中,当a的记录日期是2015-01-02,则寻找b的日期是2015-01-01,此时DATEDIFF为1。其他情况类似。 那么,这样的写法是否可行?需要测试。 例如,假设a是2015-01-02,b是2015-01-01,那么DATEDIFF返回1。此时符合条件,连接成功。这样,可以正确筛选出对应的记录。 所以,这种写法可能更简洁,但可能效率较低,因为需要计算所有可能的a和b的日期差,而不仅仅是精确的加减一天的情况。例如,如果有两个不同的b记录,它们的日期与a的日期差都是1天,那么会出现多个连接结果吗?但题目中的条件已经说明,recordDate是唯一的,所以不可能有多个b记录满足同一个a的日期差为1天。因此,这种方式是安全的。 因此,这个写法是可行的,并且正确。 所以,最终的SQL可以是: SELECT a.id FROM Weather a INNER JOIN Weather b ON DATEDIFF(a.recordDate, b.recordDate) = 1 WHERE a.temperature > b.temperature; 或者用前面提到的其他方式。 综上所述,正确的解决方案是使用自连接,并比较日期差和温度。</think>要解决这个问题,我们需要找到所有温度高于前一天的日期的 `id`。可以通过自连接或窗口函数实现。以下是分步解决方案: ### 方法一:自连接 **思路**:将天气表与自身连接,连接条件是某个日期的记录与前一天的记录。比较两者的温度,筛选出当天温度更高的记录。 **步骤**: 1. **自连接表**:将表 `Weather` 分别作为当天的记录(别名 `a`)和前一天的记录(别名 `b`)。 2. **连接条件**:确保 `a` 的日期是 `b` 的日期加一天(使用日期函数或差值判断)。 3. **筛选条件**:比较当天的温度是否高于前一天。 **SQL 语句**: ```sql SELECT a.id FROM Weather a JOIN Weather b ON DATEDIFF(a.recordDate, b.recordDate) = 1 WHERE a.temperature > b.temperature; ``` ### 方法二:窗口函数 **思路**:使用 `LAG` 窗口函数获取前一天的日期和温度,然后筛选出满足条件的记录。 **步骤**: 1. **子查询**:按日期排序,获取每条记录的前一天温度和日期。 2. **筛选条件**:当前温度高于前一天,且日期差为 1 天。 **SQL 语句**: ```sql SELECT id FROM ( SELECT id, temperature, LAG(temperature) OVER (ORDER BY recordDate) AS prev_temp, LAG(recordDate) OVER (ORDER BY recordDate) AS prev_date FROM Weather ) AS temp_table WHERE temperature > prev_temp AND DATEDIFF(recordDate, prev_date) = 1; ``` ### 结果示例 **输入**: ``` +----+------------+-------------+ | id | recordDate | Temperature | +----+------------+-------------+ | 1 | 2015-01-01 | 10 | | 2 | 2015-01-02 | 25 | | 3 | 2015-01-03 | 20 | | 4 | 2015-01-04 | 30 | +----+------------+-------------+ ``` **输出**: ``` +----+ | id | +----+ | 2 | | 4 | +----+ ``` **解释**: - `id=2` 的日期是 `2015-01-02`,前一天温度是 `10`,满足 `25 > 10`。 - `id=4` 的日期是 `2015-01-04`,前一天温度是 `20`,满足 `30 > 20`。 两种方法均能正确解决问题,选择取决于数据库支持的特性和性能需求。自连接方法更通用,窗口函数方法在大数据量下可能更高效。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值