动态规划系列(4)——子列表的和是否等于某一个数

油管上讲的最清晰的

这个问题是比较复杂的问题,假设存在一个列表,并给定一个数S,判断该列表中是否存在数的和为数S。和之前的动态规划类似,我们先从最后一位往前算,还是选择不选择的问题。考虑方程,如果选择最后一位,则看前一位的状态是否可以求出和为(S-最后一位数);如果不选择最后一位,则看前一位的状态是否可以求出和为S,最后只要选择和不选择的情况下只要一个True即可。

即下面的公式

考虑一下出口问题,如果当s为0时,说明此时肯定已经找到了,返回True;如果当i为0时,此时判断arr[0]等于s与否即可,如果不等于则输出False,等于输出True;针对上述的状态方程,还有一种情况需要考虑,如果arr[i]大于s,此时 直接考虑前面一位是否存在和为s即可。

递归解决的思路按照上述的思路即可解决,代码如下:

# -*- coding: utf-8 -*-
'''
给定一个数组和一个数字,判断这个数组中是否有数字的和为这个数字,是返回True,否返回False
'''

# 递归 
# arr数组到第i个数组是否可以得出和s
def rec_subset(arr, i , s):
    # 出口1
    if s == 0:
        return True
    # 出口2: 当前算到第一个数,就是判断第一个数,是否等于剩余的s
    elif i == 0:
        return arr[i] == s
    # 如果当前的i处的数大于s,则不考虑i处的数,我们不选择
    elif arr[i] > s:
        return rec_subset(arr, i-1, s)
    else:
        # 选择当前i处的数
        A = rec_subset(arr, i-1, s-arr[i])
        # 不选择当前i处的数
        B = rec_subset(arr, i-1, s)
        return A or B
                
if __name__ == '__main__':
    arr = [3,34,4,12,5,2]
    print(rec_subset(arr, len(arr)-1, 9))   

按照动态规划的思路解决,此时需要将每种情况下(i,s)的结果进行保存,即需要建立一个二维数组,行数为列表的长度,列数为S+1(和从0到S),此时用S表示。

数组中有几种情况,当s=0时,此时均为True;当i = 0时,除了s等于arr[i]外其余都为False。其余位置的计算按照递归中的规则进行计算,我们最后求得是,所有列表中和为S的情况,所以返回二维数组最右下角一个数的值。以图中红色框为例,subset[arr,2,4],代表当arr取前2位(包括)数时,是否存在和为4的情况。

代码:

# 二维数组保存中间所有的动态过程
def dp_subset(arr, S):
    import numpy as np
    # 构建一个二维数组
    subset = np.zeros((len(arr),S+1),dtype = bool)
    # 当s等于0时,返回True
    subset[:,0] = True
    # 第一行数除了等于他本身处为True,其余为False
    subset[0,:] = False
    subset[0,arr[0]] = True
    for i in range(1, len(arr)):
        for s in range(1, S+1):
            if arr[i] > s:
                subset[i,s] = subset[i-1,s]
            else:
                A = subset[i-1, s-arr[i]]    
                B = subset[i-1, s]
                subset[i,s] = A or B
    r, c = subset.shape
    return subset[r-1,c-1]

if __name__ == '__main__':
    arr = [3,34,4,12,5,2]
    print(dp_subset(arr, 12))

猜你喜欢

转载自blog.csdn.net/legend_hua/article/details/81255270