Hard
题目描述
你正在安装一个广告牌,希望它有最大的高度。广告牌将有两个钢支柱,一个在每一边。每个钢支柱必须是相等的高度。
给你一组可以焊接在一起的钢筋。例如,如果你有长度为 1、2 和 3 的钢筋,你可以将它们焊接在一起制作一个长度为 6 的支柱。
返回广告牌安装的最大可能高度。如果无法支撑广告牌,返回 0。
示例 1:
输入:rods = [1,2,3,6]
输出:6
解释:我们有两个不相交的子集 {1,2,3} 和 {6},它们的和相等 = 6。
示例 2:
输入:rods = [1,2,3,4,5,6]
输出:10
解释:我们有两个不相交的子集 {2,3,5} 和 {4,6},它们的和相等 = 10。
示例 3:
输入:rods = [1,2]
输出:0
解释:广告牌无法支撑,所以我们返回 0。
提示:
1 <= rods.length <= 201 <= rods[i] <= 1000sum(rods[i]) <= 5000
解题思路
这是一个典型的动态规划问题。题目要求将钢筋分成三组:左支柱、右支柱和不使用的钢筋,使得左右支柱高度相等且最大。
核心思路:
- 对于每根钢筋,我们有三种选择:放在左支柱、放在右支柱或者不使用
- 我们需要维护所有可能的"左支柱高度 - 右支柱高度"的差值状态
- 使用哈希表记录每个差值对应的左支柱的最大高度
状态转移:
dp[diff]表示当左右支柱高度差为diff时,左支柱的最大高度- 对于每根钢筋
rod:- 放在左支柱:
new_dp[diff + rod] = max(new_dp[diff + rod], dp[diff] + rod) - 放在右支柱:
new_dp[diff - rod] = max(new_dp[diff - rod], dp[diff]) - 不使用:保持原状态
- 放在左支柱:
优化细节:
- 初始状态:
dp[0] = 0(差值为0时,左支柱高度为0) - 最终答案:
dp[0](差值为0表示左右支柱高度相等) - 由于差值可能为负数,我们使用偏移量或者允许负键的数据结构
时间复杂度 O(n × sum),其中 n 是钢筋数量,sum 是所有钢筋长度之和。空间复杂度 O(sum)。
代码实现
class Solution {
public:
int tallestBillboard(vector<int>& rods) {
unordered_map<int, int> dp;
dp[0] = 0;
for (int rod : rods) {
unordered_map<int, int> new_dp = dp;
for (auto& [diff, height] : dp) {
// 放在左支柱
new_dp[diff + rod] = max(new_dp[diff + rod], height + rod);
// 放在右支柱
new_dp[diff - rod] = max(new_dp[diff - rod], height);
}
dp = new_dp;
}
return dp[0];
}
};
class Solution:
def tallestBillboard(self, rods: List[int]) -> int:
dp = {0: 0} # diff -> max left height
for rod in rods:
new_dp = dp.copy()
for diff, height in dp.items():
# 放在左支柱
new_diff = diff + rod
new_dp[new_diff] = max(new_dp.get(new_diff, 0), height + rod)
# 放在右支柱
new_diff = diff - rod
new_dp[new_diff] = max(new_dp.get(new_diff, 0), height)
dp = new_dp
return dp[0]
public class Solution {
public int TallestBillboard(int[] rods) {
var dp = new Dictionary<int, int>();
dp[0] = 0;
foreach (int rod in rods) {
var newDp = new Dictionary<int, int>(dp);
foreach (var kvp in dp) {
int diff = kvp.Key;
int height = kvp.Value;
// 放在左支柱
int newDiff = diff + rod;
if (!newDp.ContainsKey(newDiff)) newDp[newDiff] = 0;
newDp[newDiff] = Math.Max(newDp[newDiff], height + rod);
// 放在右支柱
newDiff = diff - rod;
if (!newDp.ContainsKey(newDiff)) newDp[newDiff] = 0;
newDp[newDiff] = Math.Max(newDp[newDiff], height);
}
dp = newDp;
}
return dp.GetValueOrDefault(0, 0);
}
}
var tallestBillboard = function(rods) {
let dp = new Map();
dp.set(0, 0);
for (let rod of rods) {
let newDp = new Map(dp);
for (let [diff, height] of dp) {
// 放在左支柱
let newDiff = diff + rod;
newDp.set(newDiff, Math.max(newDp.get(newDiff) || 0, height + rod));
// 放在右支柱
newDiff = diff - rod;
newDp.set(newDiff, Math.max(newDp.get(newDiff) || 0, height));
}
dp = newDp;
}
return dp.get(0) || 0;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n × sum) | n 为钢筋数量,sum 为所有钢筋长度之和,每根钢筋需要遍历所有已有状态 |
| 空间复杂度 | O(sum) | 哈希表存储所有可能的差值状态,最多有 O(sum) 个不同的差值 |