这个问题是比较复杂的问题,假设存在一个列表,并给定一个数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))