zoukankan      html  css  js  c++  java
  • leetcode 956 最高广告牌 (求数组中和相等且最大的两个子数组)

    你正在安装一个广告牌,并希望它高度最大。这块广告牌将有两个钢制支架,两边各一个。每个钢支架的高度必须相等。
    你有一堆可以焊接在一起的钢筋 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。

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

    提示:
    0 <= rods.length <= 20
    1 <= rods[i] <= 1000
    钢筋的长度总和最多为 5000

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/tallest-billboard
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    思路:

    将问题转化为求数组和为0时的组合
     
    对任何一个数,可以用三种方式对待它,乘以1,-1或0,目标是求和为0时的最大正数和
     
    例如,[1,2,3], 可以对1,2乘以1,3乘以-1,此时和为0, 最大正数和为1+2=3
    用字典来存储每一步的结果,键和值分别是(k:v) 总和以及正数和,
    初始化时dp={0:0},表示和为0时的最大长度为0
    那么最后只需要求dp[0]的最大值就ok辣
     
    遍历所有钢筋:
    对每根钢筋都有三种处理方式:加,减,丢 (对应乘以1,-1或0)
     
    如:[1,2,3]
    第一步: 用钢筋1,对初始的0,操作
    如果加,那么总和是1,正数是1;如果减,总和是-1,正数0;如果丢,维持不变;更新dp={0:0, 1:1, -1:0}
    第二步: 用钢筋2,对第一步中dp的键0,1,-1的基础上分别进行“加,减,丢 ”的操作
    在0:0基础上,如果加,也就是变为2:2;如果减,变为-2:0;如果丢,变成0:0
    类似的,在1:1基础上,加减丢变为3:3-1:1,1:1
    类似的,在-1:0基础上,加减丢变为1:2-3:0,-1:0
    每个键取较大值,用粗体标识了,然后更新dp={0:0, 1:2, 2:2, -1:1, 3:3, -2:0, -3:0}
    总和为1时,相比第一步时的正数和为1,第二步时正数和变为了2,将dp[1]修改为更大的2
    总和为-1时,相比第一步时的正数和为0,第二步时正数和变为了1,将dp[-1]修改为更大的1
    最后返回dp[0]

    作者:wzNote
    链接:https://www.jianshu.com/p/fffe353d45c7
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    以下为错误代码

    # 我的代码如下,并不能ac,原因在于更新列表时候,要用最新的值(键要用老的)
    class Solution:
        def tallestBillboard(self, rods):
            dp = {0: 0}
            for i in rods:
                dp1 = dp.copy()
                for k in list(dp1.keys()):
                    dp[k - i] = max(dp1.get(k - i, 0), dp1[k])
                    dp[k + i] = max(dp1.get(k + i, 0), dp1[k] + i)
                #print(dp)
                #print()
            return dp[0]
    
    # 正确
    print(Solution().tallestBillboard([1,2,3,6])) 
    #这个用例会错误
    print(Solution().tallestBillboard([61, 45, 43, 54, 40, 53, 55, 47, 51, 59, 42])) 
    

    原因在于第三次迭代的时候dp数组结果为{0: 0, -61: 0, 61: 61, -45: 0, 45: 45, -106: 0, -16: 45, 16: 61, 106: 106, -43: 0, 43: 43, -104: 0, -18: 43, 18: 61, 104: 104, -88: 0, -2: 43, 2: 45, 88: 88, -149: 0, -63: 43, -59: 45, 27: 88, -27: 61, 59: 104, 63: 106, 149: 149}

    可以看到有个键-106,106在前面,这个时候第四次迭代的值是54,会更新键-52:54,52:106 考虑这个时候碰到键-2,2的时候则更新为52:106, -52:54
    我没有考虑这个-106 106(因为用的老值),所以遇到-2,2的时候是第一次更新,变为52:97,-52:45然后就错了

    正确代码如下

    class Solution:
        def tallestBillboard(self, rods):
            dp = {0: 0}
            for i in rods:
                # 浅拷贝和深拷贝请看https://www.cnblogs.com/alimy/p/10374923.html
                dp1 = dp.copy()
                for k in list(dp1.keys()):
                    # 把这里的dp1改成了dp,那么获取的就是最新值啦~
                    dp[k + i] = max(dp.get(k + i, 0), dp1[k] + i)
                    dp[k - i] = max(dp.get(k - i, 0), dp1[k])
            return dp[0]
    
    # 来自https://www.jianshu.com/p/fffe353d45c7
    class Solution(object):
        def tallestBillboard(self, rods):
            dp = {0:0}
            for r in rods:
                # 注意python语言的特性,下面这一行的pre_sum, pos_sum就是上一次迭代dp的老值,不会随dp变了他就变了
                for pre_sum, pos_sum in list(dp.items()):
                    dp[pre_sum-r] = max(dp.get(pre_sum-r, 0), pos_sum)
                    dp[pre_sum + r] = max(dp.get(pre_sum+r,0), pos_sum+r)
                print(dp)
                print()
            return dp[0]
    
    对python语言这个特性的解释如下

    Python连续赋值需要注意的地方
    在python中是可以使用连续赋值的方式来一次为多个变量进行赋值的,比如:

    a = b = c = 1
    a, b, c = 1, 1, 1
    

    这些都可以完成变量的赋值,但是就有一个问题了,比如:

    a = 3
    a, b = 1, a
    

    如果按照正常的思维逻辑,先进行a = 1,在进行b = a,最后b应该等于1,但是这里b应该等于3,因为在连续赋值语句中等式右边其实都是局部变量,而不是真正的变量值本身,比如,上面例子中右边的a,在python解析的时候,只是把变量a的指向的变量3赋给b,而不是a=1之后a的结果,这一点刚开始学python的人可能容易误解,再举一个Leetcode里链表的例子理解就更深了。 见链接,建议画图
     
    假如要对一个链表进行翻转,就比如把1—>2->3->4转化为4->3->2->1
    对于这个问题很简单,只要反转指针就可以了,假如链表结构为:

    class ListNode:
        def __init__(self, x):
            self.val = x
            self.next = None
    

    我们可以用很简单的三行代码完成这个过程:

    def reverseList(self, head):
        L = ListNode(float("-inf"))
        while head:
              L.next, head.next, head = head, L.next, head.next
        return L.next
    

    这里的L是指向一个新建的结点,因为python没有指针的概念,所以用一个额外的结点来代替头指针,这里的核心代码就是中间那一行三个变量的连续赋值,如果单独一句句来理解的话,最后肯定是想不通的,在这里,假设head结点是链表串’1->2->3->4’的头结点,先用新的L结点的next指针指向head的第一个结点‘1’,之后将L.next(第一次也就是空)赋给了head的next指针,之后再把head的next指针(注意,这里的next指针还是指向‘2’的,而不是空)赋给head,相当于next向前移一位,这一步相当于一个串变成了两个:

    L:‘-inf’->‘1’
    head:‘2’->‘3’->‘4’->‘5’













    种一棵树最好的时间是十年前,其次是现在。
  • 相关阅读:
    (原创)C++ 同步队列
    (原创)用C++11的std::async代替线程的创建
    C语言宏应用-------#define STR(X) #X
    c++中数据表如何转成业务实体--map和结构体的相互转换
    线程池的陷阱
    java中map和对象互转工具类的实现示例
    3.python元类编程
    人生苦短,我用python(目录)
    11.多线程、多进程和线程池编程
    10.python3实用编程技巧进阶(五)
  • 原文地址:https://www.cnblogs.com/islch/p/13470241.html
Copyright © 2011-2022 走看看