leetcode
leetcode 851 ~ 900
最高的广告牌

最高的广告牌

难度:

标签:

题目描述

代码结果

运行时间: 151 ms, 内存: 16.7 MB


// 思路:同样是使用动态规划的方法,通过 Java Stream API 来实现。我们利用 Stream 的操作来遍历和更新 dp 集合。
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

public class BillboardHeightStream {
    public int tallestBillboard(int[] rods) {
        Map<Integer, Integer> dp = new HashMap<>();
        dp.put(0, 0);
        for (int rod : rods) {
            Map<Integer, Integer> cur = dp.entrySet().stream().collect(Collectors.toMap(
                Map.Entry::getKey, Map.Entry::getValue));
            cur.forEach((d, h) -> {
                dp.merge(d + rod, h, Math::max);
                dp.merge(Math.abs(d - rod), h + Math.min(d, rod), Math::max);
            });
        }
        return dp.getOrDefault(0, 0);
    }
}

解释

方法:

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

时间复杂度:

O(2^(n/2))

空间复杂度:

O(2^(n/2))

代码细节讲解

🦆
为什么在更新字典时需要使用临时列表`list(f.items())`来避免在迭代中修改字典?能否直接在迭代`f`的过程中更新?
在Python中,直接在迭代字典的过程中修改字典(如增加或删除键值对)可能会导致运行时错误,因为这会改变字典的大小,进而可能影响迭代的顺序和完整性。使用`list(f.items())`创建字典项的临时拷贝,可以安全地遍历字典项而不影响原字典结构。在完成所有更新后,这些变更可以安全地应用到原字典。这种方法确保代码在修改字典时不会因为迭代器失效而引发错误。
🦆
在函数`h(rods)`中,为何选择将钢筋加到一边或从一边减去,并且如何保证这两种情况能够覆盖所有可能的子集和差值?
在`h(rods)`函数中,选择将钢筋加到一边或从一边减去的原因是要探索所有可能的高度差组合。这种方法模拟了'放置'和'不放置'钢筋到特定一边的所有情况,从而可以计算两边的高度差。将钢筋加到一边意味着这根钢筋贡献了高度,而从一边减去则意味着它被假设在另一边。这样不仅覆盖了所有可能的钢筋组合,也计算了每种组合对应的最大可能高度。因此,通过这两种操作,可以确保涵盖所有子集的高度差情况。
🦆
题解中提到通过分治法将钢筋分为两部分,这种分法是否总是最优的?会不会存在某些情况下,不均等的分割会得到更好的结果?
分治法将钢筋分为两部分主要是为了减少问题规模和简化问题的复杂度。这种方法通常不保证总是最优的,因为理论上不均等分割可能在某些特定情况下找到更好的组合。然而,均等分割有助于平衡两边的计算量和可能性,使得在合并结果时更容易处理和匹配。实际应用中,是否采用不均等分割取决于具体的问题特性和数据分布,有时适当的不均等分割可能会考虑到更多的特定情况,从而可能提高解的质量。

相关问题