最高的广告牌

标签: 数组 动态规划

难度: Hard

你正在安装一个广告牌,并希望它高度最大。这块广告牌将有两个钢制支架,两边各一个。每个钢支架的高度必须相等。

你有一堆可以焊接在一起的钢筋 rods。举个例子,如果钢筋的长度为 123,则可以将它们焊接在一起形成长度为 6 的支架。

返回 广告牌的最大可能安装高度 。如果没法安装广告牌,请返回 0 。

示例 1:

输入:[1,2,3,6]
输出:6
解释:我们有两个不相交的子集 {1,2,3} 和 {6},它们具有相同的和 sum = 6。

示例 2:

输入:[1,2,3,4,5,6]
输出:10
解释:我们有两个不相交的子集 {2,3,5} 和 {4,6},它们具有相同的和 sum = 10。

示例 3:

输入:[1,2]
输出:0
解释:没法安装广告牌,所以返回 0。

提示:

  1. 0 <= rods.length <= 20
  2. 1 <= rods[i] <= 1000
  3. sum(rods[i]) <= 5000

Submission

运行时间: 151 ms

内存: 16.7 MB

class Solution:
    def tallestBillboard(self, rods: List[int]) -> int:
        def h(rods):
            f = defaultdict(int)
            f[0] = 0
            for x in rods:
                for diff, height in list(f.items()):
                    f[diff+x] = max(f[diff+x], height+x)
                    f[diff-x] = max(f[diff-x], height)
            return f 
        n = len(rods)
        l = h(rods[:n//2])
        r = h(rods[n//2:])
        ans = 0
        for i in l:
            if -i in r:
                ans = max(ans, l[i]+r[-i])
        return ans

Explain

此题解使用了分治加动态规划的方法。首先将钢筋数组分为两部分,对每部分独立求解可能的差值和相应的最大高度。这里使用一个字典来存储从差值到最大高度的映射。对于每根钢筋,更新两种情况:将它加到一边或从一边减去。这样,可以得到包含所有可能差值的字典。然后,将两部分的结果合并,查找两个字典中相反的键(即差值相等但符号相反,表示两边可以平衡),并计算这些情况的最大高度。最终结果是所有匹配差值组合中的最大高度。

时间复杂度: O(2^(n/2))

空间复杂度: O(2^(n/2))

class Solution:
    def tallestBillboard(self, rods: List[int]) -> int:
        def h(rods):
            f = defaultdict(int)  # 创建一个默认值为int的字典
            f[0] = 0  # 初始化差值为0的情况,最大高度也是0
            for x in rods:  # 遍历每根钢筋
                for diff, height in list(f.items()):  # 临时列表避免在迭代中修改字典
                    f[diff+x] = max(f[diff+x], height+x)  # 将钢筋加到一边的情况
                    f[diff-x] = max(f[diff-x], height)  # 将钢筋减去的情况
            return f 
        n = len(rods)
        l = h(rods[:n//2])  # 处理前半部分
        r = h(rods[n//2:])  # 处理后半部分
        ans = 0
        for i in l:  # 遍历第一部分的结果
            if -i in r:  # 查找相反差值的存在
                ans = max(ans, l[i]+r[-i])  # 如果存在,更新最大高度
        return ans

Explore

在Python中,直接在迭代字典的过程中修改字典(如增加或删除键值对)可能会导致运行时错误,因为这会改变字典的大小,进而可能影响迭代的顺序和完整性。使用`list(f.items())`创建字典项的临时拷贝,可以安全地遍历字典项而不影响原字典结构。在完成所有更新后,这些变更可以安全地应用到原字典。这种方法确保代码在修改字典时不会因为迭代器失效而引发错误。

在`h(rods)`函数中,选择将钢筋加到一边或从一边减去的原因是要探索所有可能的高度差组合。这种方法模拟了'放置'和'不放置'钢筋到特定一边的所有情况,从而可以计算两边的高度差。将钢筋加到一边意味着这根钢筋贡献了高度,而从一边减去则意味着它被假设在另一边。这样不仅覆盖了所有可能的钢筋组合,也计算了每种组合对应的最大可能高度。因此,通过这两种操作,可以确保涵盖所有子集的高度差情况。

分治法将钢筋分为两部分主要是为了减少问题规模和简化问题的复杂度。这种方法通常不保证总是最优的,因为理论上不均等分割可能在某些特定情况下找到更好的组合。然而,均等分割有助于平衡两边的计算量和可能性,使得在合并结果时更容易处理和匹配。实际应用中,是否采用不均等分割取决于具体的问题特性和数据分布,有时适当的不均等分割可能会考虑到更多的特定情况,从而可能提高解的质量。