问题描述:
你正在安装一个广告牌,并希望它高度最大。这块广告牌将有两个钢制支架,两边各一个。每个钢支架的高度必须相等。
你有一堆可以焊接在一起的钢筋 rods
。举个例子,如果钢筋的长度为1
、2
和 3
,则可以将它们焊接在一起形成长度为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。
提示:
0 <= rods.length <= 20
1 <= rods[i] <= 1000
- 钢筋的长度总和最多为
5000
问题分析:
(1)方法一:
把题目的的意思简化一下,其实就是这一组数据分成3组,其中两组的和一样(最大的哦),另外一组无所谓了。不妨给每一个数字打标签分别乘,1
、0
、-1
,总和加起来为0
,且,正整数和达到最大(每到一个值,就有三种情况考虑,如下图)
在实现方面,就可以考虑,深度优先,或者是广度优先+动规来实现。这种方法实现比较简单,但是计算量较大,开辟的空间也大,最坏的情况下为
3
n
3^n
3n 。比较好的是,这个方法仍然可以提交AC的。具体方法有点类似背包问题:
(1)现在设置一个dp
,可以python
字典表示,k
表示,整个序列乘上标签,之后的和,value
表示这些序列中正数的和。从上图可以看出来初始化为{0:0}
。
(2)接下来,就是每处理一个数,就处理一层。简单的理解就是,在上层产生的结果中,如果继续向下分析,在前面的基础上,每个结果有三种可能。
(3)处理的方法为:
cur = collections.defaultdict(int)
for s in dp:
cur[s + i] = max(dp[s] + i, cur[s + i]) # 标记为 1 的情况,并取最大值
cur[s] = max(dp[s], cur[s]) # 标记为 0 的情况,并取最大值
cur[s - i] = max(dp[s], cur[s - i]) # 标记为 -1 的情况,并取最大值
dp = cur # 更新dp
cur
表示当前正在处理的层。dp
表示上一层。从这个过程可以看出,其实也是一个动规过程,依赖前面的结果,构成子问题。虽然类似于穷举法,但是在计算过程相同的可能只保留了一个。最后,直接返回dp[0]
即可。参考链接在文章下面。
Python3实现:
import collections
class Solution:
def tallestBillboard(self, rods):
dp = dict()
dp[0] = 0 # 初始化dp
for i in rods:
cur = collections.defaultdict(int)
for s in dp: # 分三种情况考虑
cur[s + i] = max(dp[s] + i, cur[s + i])
cur[s] = max(dp[s], cur[s])
cur[s - i] = max(dp[s], cur[s - i])
dp = cur
return dp[0] # 返回结果
if __name__ == '__main__':
solu = Solution()
rods = [1, 2, 3, 4, 5, 6]
print(solu.tallestBillboard(rods))
(2)方法二:
现在看一下官方的解答,官方的方法是使用递归实现的,其思想采用了深度优先 加动规 的方法。
(1)令 dp[i][s]
表示使用rods[0:j] (j >= i)
时最优解,而s
表示,在当前路径中结合3
种标签(1,0,-1)
的和。
(2)很显然,把每个路径走到头,如果s=0
,说明这个路径是可行的,如果不为0
,说明此路径是不可行的。
(3)当然,走到头,会有很多种可能,所以选择一个最大的作为结果,即可。
(4)所以,递推公式可以表示为:dp[i][s] = max(dp[i+1][s], dp[i+1][s-rods[i]], rods[i] + dp[i+1][s+rods[i]])
。
(5)这种思想采用了深度优先 ,感觉也是穷举法。
Python3实现:
from functools import lru_cache
class Solution:
def tallestBillboard(self, rods):
@lru_cache(None)
def dp(i, s):
if i == len(rods):
return 0 if s == 0 else float('-inf')
# 三情况取最大
return max(dp(i + 1, s), dp(i + 1, s - rods[i]), dp(i + 1, s + rods[i]) + rods[i])
return dp(0, 0)
if __name__ == '__main__':
solu = Solution()
rods = [1, 2, 3, 4, 5, 6]
print(solu.tallestBillboard(rods))
声明: 总结学习,有问题或不当之处,可以批评指正哦,谢谢。