416分割等和子集

题目:给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意: 每个数组中的元素不会超过 100,数组的大小不会超过 200
来源:https://leetcode-cn.com/problems/partition-equal-subset-sum/

法一:动态规划

思路:首先写状态转移方程的时候,如果采用类似312戳气球的方法,即由内到外,由小到大,是难以实现的,原因有二,一是假如题目中的数组元素有6个,则dp[1]即长度为1的子集有6个,dp[2]长度为2的子集有15个,dp[3]长度为3的子集有20个,从dp[1]到dp[2]状态转移的过程中无法实现。二是由于长度不等,难以写成dp数组的形式。说到底是因为,dp[i]定义成了长度为i的子集的和,而不同长度的子集间由于子集的个数不同,难以写出状态转移方程。

          由此定义dp[i]为nums中从0到i的子集的和,则dp[i+1]从dp[i]转移过来的时候,由于dp[i]的情况很多,由此确定需要二维数组来记录,二维数组的列用来记录所有的和,如果这个和存在记为True,否则为False。

from typing import List
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        n=len(nums)
        target=sum(nums)
        if(target%2!=0):
            return False
        target//=2
        dp=[[False]*(target+1) for _ in range(n)]
        # 设置边界条件非常重要,将第一列(即和为0的列)设置为True,
        dp[0][0]=True
        # 先遍历第一行
        for i in range(1,target+1):
            if(nums[0]==i):
                dp[0][i]=True
                break
        # 遍历其余行
        for i in range(1,n):
            for j in range(target+1):
                # 如果当前的目标和值大于等于要遍历的值,则将目标和值减去当前值看上一行和为dp[i-1][j-nums[i]]的为True还是False
                if(j>=nums[i]):
                    dp[i][j]=dp[i-1][j] or (dp[i-1][j-nums[i]])
                # 否则说明目标值小于要遍历的值,直接等于上一行的结果
                else:
                    dp[i][j]=dp[i-1][j]
            # 如果最后一列出现True了,说明存在组合成目标值的和.直接返回True
            # if dp[i][-1] == True:
            #     return True
        return dp[-1][-1]
if __name__ == '__main__':
    duixiang = Solution()
    a = duixiang.canPartition([1, 5, 11, 5])
    print(a)
View Code

来源:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/dong-tai-gui-hua-kong-jian-you-hua-zhu-xing-jie--2/

法二:备忘录方法  非常巧妙

思路:利用ans记录之前的计算结果,每次都和之前的求和后判别是否满足条件。

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        target, remain = divmod(sum(nums), 2)
        if remain:  # 如果不能整除直接返回
            return False
        ans = {0}
        for i in nums:
            for j in list(ans): # 循环中会改变ans
                j += i
                if j == target:  # 提前结束
                    return True
                ans.add(j)  # 之前的结果加当前数能得到的结果
        return False
View Code

ttt

posted on 2020-01-28 15:31  吃我一枪  阅读(243)  评论(0编辑  收藏  举报

导航