数据结构算法刷题:背包问题

文章讲述了如何将给定的整数和问题转化为递推形式,利用动态规划(dp)解决求解target为目标和的组合方式。文中提到的`Solution`类展示了两种不同场景下的解决方案,一种是至多等于target,另一种是至少等于target。同时提到了`cache`用于避免重复计算,以及在完全背包问题中的应用。

整数和是p,负数和是s-p,那么target = p - (s-p),求出p = (s+t)//2

class Solution:

    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        target += sum(nums)

        if target < 0 or target % 2: #target 一定是偶数而且是大于0 

            return 0

        target = target // 2

        n = len(nums)

        @cache   #cache是将传参和返回记录下来,避免了重复计算

        def dfs(i,c):

            if i < 0:

                return 1 if c == 0 else 0 #如果c小于0,那就没有找到,返回0,c==0返回1

            if nums[i] > c: #当前值大于c,不能选

                return dfs(i-1,c)

            return dfs(i-1,c)+dfs(i-1,c-nums[i]) #返回选或者不选的和

        return dfs(n-1,target)

改成递推的形式

class Solution:

    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        target += sum(nums)

        if target < 0 or target % 2:

            return 0

        target = target // 2

        n = len(nums)

        dp = [[0]*(target+1) for i in range(n+1)]

        dp[0][0] = 1 #初始化

        for i,x in enumerate(nums):

            for c in range(target+1):

                if c < x: #不选

                    dp[i+1][c] = dp[i][c]

                else: #选+不选

                    dp[i+1][c] = dp[i][c] + dp[i][c-x]

        return dp[-1][-1]

class Solution:

    def coinChange(self, coins: List[int], amount: int) -> int:

        #完全背包,对于完全背包,每次选择之后,还可以继续选。

        n = len(coins)

        @cache

        def dfs(i,c):

            if i < 0:

                return 0 if c == 0 else inf #如果当前不满足条件就返回无穷

            if c < coins[i]: #不选

                return dfs(i-1,c)

            return min(dfs(i-1,c),dfs(i,c-coins[i])+1) #选的时候不能是i-1,因为选了可以再选

        a = dfs(n-1,amount)

        return a if a < inf else -1

改成递推的形式

class Solution:

    def coinChange(self, coins: List[int], amount: int) -> int:

        n = len(coins)

        dp = [[inf]*(amount+1) for i in range(n+1)]

        dp[0][0] = 0

        for i,x in enumerate(coins):

            for c in range(amount+1):

                if x > c:

                    dp[i+1][c] = dp[i][c] #不选,往下走

                else:

                    dp[i+1][c] =min(dp[i][c],dp[i+1][c-x]+1) #选了还可以继续选i+1

        ans = dp[-1][-1]

        return ans if ans < inf else -1    

在例题494中,如果是至多是target。那就在边界条件时不需要判断,初始化dp全部为1.

class Solution:

    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        target += sum(nums)

        if target < 0 or target % 2:

            return 0

        target = target // 2

        n = len(nums)

        dp = [[1]*(target+1) for i in range(n+1)]

        for i,x in enumerate(nums):

            for c in range(target+1):

                if c < x: #不选

                    dp[i+1][c] = dp[i][c]

                else: #选+不选

                    dp[i+1][c] = dp[i][c] + dp[i][c-x]

        return dp[-1][-1]

class Solution:

    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        target += sum(nums)

        if target < 0 or target % 2: #target 一定是偶数而且是大于0 

            return 0

        target = target // 2

        n = len(nums)

        @cache   #cache是将传参和返回记录下来,避免了重复计算

        def dfs(i,c):

            if i < 0:

                return 1 

            if nums[i] > c: #当前值大于c,不能选

                return dfs(i-1,c)

            return dfs(i-1,c)+dfs(i-1,c-nums[i]) #返回选或者不选的和

        return dfs(n-1,target)

如果是至少为target,那就是比target大也可以。

class Solution:

    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        target += sum(nums)

        if target < 0 or target % 2: #target 一定是偶数而且是大于0 

            return 0

        target = target // 2

        n = len(nums)

        @cache   #cache是将传参和返回记录下来,避免了重复计算

        def dfs(i,c):

            if i < 0:

                return 1 if c <= 0 else 0

            return dfs(i-1,c)+dfs(i-1,c-nums[i]) #不会判断nums[i]>c的情况

        return dfs(n-1,target)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值