继续讲故事~~

  上次讲到我们的主人公丁丁,用神奇的动态规划法解决了杂货店老板的两个找零钱问题,得到了老板的肯定。之后,他就决心去大城市闯荡了,看一看外面更大的世界。

  这天,丁丁刚回到家,他的弟弟小连就拦住了他,“老哥,有个问题想请教你。”对于一向数学见长的小连,这次竟然破天荒的来问自己问题,丁丁感到不可思议:他俩一个以计算机见长,一个以数学见长,各自心里都有点小骄傲,不会轻易地向对方问问题。丁丁迟疑了一会儿,慢慢说道:“有什么问题是我们数学小天才解决不了的?”

  原来小连刚上高一,正在学数学中的集合,这不,今天他从一本算法书上看到一道题,想了很久都没有想出来。他把题目给了丁丁看:

对于某个给定值M,如何从某个给定的正整数集合S中找个一个子集合s,使得该子集和为给定值M。如M=7,S={1,3,4,5},则s={3,4}.

  看到这道题目,丁丁脑海中掠过“动态规划法”的念头,对于动态规划法,他已经是轻车熟路了,但是对于究竟能否用动态规划法解决这个问题,他一时也没主意。于是,他对小连说道:“这题也许可以用动态规划法解决,不过我得好好想一想。”小连点点头,他还是蛮相信他的哥哥的。

  丁丁走进自己的房间,拿出草稿纸,开始了思考的旅程:

对于S={a1,a2,...,an},每个元素只有取与不取两种情况,再考虑它们的和是否等于M,但是这样的情况共有2^n中,这种算法的效率显然是不行的。

  换条思路,令subset(i,j)表示S中前i个元素的子集和等于j的情况,则

  • 若S[i] > j,则S[i]不在子集s中。
  • 若S[i] <= j, 则有以下两种情况:一种情况是S[i]不在子集s中,则subset(i, j) = subset(i-1, j); 一种情况是S[i]在子集s中,则subset(i, j)= subset(i-1, j-S[i]).

  这样就有了这个问题的子结构问题,因此,只需要确定初始情况即可:

对于i=0,1,2,...,n,有subset(i, 0)=True, 对于j=1,2,...,M, 有subset(0, j)=False.

因此,利用动态规划法,就能得到(n+1)*(M+1)的真值表了,而答案就是subset(n, M). 算法有了,Python代码自然也有了:

import numpy as np

# A Dynamic Programming solution for subset sum problem
# Returns true if there is a subset of set with sum equal to given sum def isSubsetSum(S, n, M):
# The value of subset[i, j] will be
# true if there is a subset of
# set[0..j-1] with sum equal to i
subset = np.array([[True]*(M+1)]*(n+1)) # If sum is 0, then answer is true
for i in range(0, n+1):
subset[i, 0] = True # If sum is not 0 and set is empty,
# then answer is false
for i in range(1, M+1):
subset[0, i] = False # Fill the subset table in bottom-up manner
for i in range(1, n+1):
for j in range(1, M+1):
if j < S[i-1]:
subset[i, j] = subset[i-1, j]
else:
subset[i, j] = subset[i-1, j] or subset[i-1, j-S[i-1]] # print the True-False table
for i in range(0, n+1):
for j in range(0, M+1):
print('%-6s'%subset[i][j], end=" ")
print(" ") if subset[n, M]:
print("Found a subset with given sum")
else:
print("No subset with given sum") # test
st = [1, 3, 4, 5]
n = len(st)
sm = 7
isSubsetSum(st, n, sm)

输出结果如下:

True False False False False False False False

True True False False False False False False

True True False True True False False False

True True False True True True False True

True True False True True True True True

Found a subset with given sum

  那么,怎样求解子集s中的元素呢?也许可以用回溯法(backtracing),他这样想到,不过,他还是决定把剩余部分交给弟弟小连。

  几分钟后,当小连看到丁丁的解法后,兴奋地直跳起来。对于计算机编程,他也是有相当大的兴趣的,不过当务之急是解决哥哥剩下来的问题,那就是找出s中的元素。他想试着从输出的真值表入手:

对于subset(i, j) = subset(i-1, j)=True,则元素S[i]不在子集s中。对于subset(i,j)=True而subset(i-1, j)=False,则元素S[i]必定在子集s中, 此时subset(i-1, j-S[i])=True,这样就能通过递归法找到s中的元素了。对于这个问题,只要从subset(n, M)开始即可。

他觉得自己的思路是可行的,于是就在哥哥的程序上修改了起来:

import numpy as np

# A Dynamic Programming solution for subset sum problem
# Returns true if there is a subset of set with sum equal to given sum def isSubsetSum(S, n, M):
# The value of subset[i, j] will be
# true if there is a subset of
# set[0..j-1] with sum equal to i
subset = np.array([[True]*(M+1)]*(n+1)) # If sum is 0, then answer is true
for i in range(0, n+1):
subset[i, 0] = True # If sum is not 0 and set is empty,
# then answer is false
for i in range(1, M+1):
subset[0, i] = False # Fill the subset table in bottom-up manner
for i in range(1, n+1):
for j in range(1, M+1):
if j < S[i-1]:
subset[i, j] = subset[i-1, j]
else:
subset[i, j] = subset[i-1, j] or subset[i-1, j-S[i-1]] # print the True-False table
for i in range(0, n+1):
for j in range(0, M+1):
print('%-6s'%subset[i][j], end=" ")
print(" ") if subset[n, M]:
print("Found a subset with given sum")
sol = []
# using backtracing to find the solution
i = n
while i >= 0:
if subset[i, M] and not subset[i-1, M]:
sol.append(S[i-1])
M -= st[i-1]
if M == 0:
break
i -= 1
print('The solution is %s.' % sol)
else:
print("No subset with given sum") # test
st = [1, 3, 4, 5]
n = len(st)
sm = 7
isSubsetSum(st, n, sm)

输出结果如下:

True False False False False False False False

True True False False False False False False

True True False True True False False False

True True False True True True False True

True True False True True True True True

Found a subset with given sum

The solution is [4, 3].

  终于解决了这个问题,小连长舒一口气,而站在一旁的丁丁,看着弟弟的程序,也露出了满意的微笑~~

  晚饭后,哥俩正坐在门口的大树下乘凉,一旁的大雄急匆匆地跑过来来他俩帮忙。原来,他也碰到了一道难题,题目是这样的:

对于一个由若干个正整数组成的集合S,如何将S划分成两部分,使得两部分的和一样?

丁丁和小连看了题目,微微一笑,因为答案就在他们刚才解决的问题中。那么,亲爱的读者,你能尝试着解决这道问题吗?

注意:本人现已开通两个微信公众号: 用Python做数学(微信号为:python_math)以及轻松学会Python爬虫(微信号为:easy_web_scrape), 欢迎大家关注哦~~

动态规划法(三)子集和问题(Subset sum problem)的更多相关文章

  1. Subset sum problem

    https://en.wikipedia.org/wiki/Subset_sum_problem In computer science, the subset sum problem is an i ...

  2. [Swift]LeetCode416. 分割等和子集 | Partition Equal Subset Sum

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  3. Sum Problem

    2018-04-22 19:59:52 Sum系列的问题是Leetcode上的一个很经典的系列题,这里做一个简单的总结. 167. Two Sum II - Input array is sorted ...

  4. Light OJ 1272 Maximum Subset Sum 高斯消元 最大XOR值

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u011686226/article/details/32337735 题目来源:problem=12 ...

  5. summary of k Sum problem and solutions in leetcode

    I found summary of k Sum problem and solutions in leetcode on the Internet. http://www.sigmainfy.com ...

  6. HDu 1001 Sum Problem 分类: ACM 2015-06-19 23:38 12人阅读 评论(0) 收藏

    Sum Problem Time Limit: 1000/500 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total ...

  7. HD2058The sum problem

    The sum problem Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...

  8. Maxmum subsequence sum problem

    We have a lot of ways to solve the maximum subsequence sum problem, but different ways take differen ...

  9. HDU 2058 The sum problem(枚举)

    The sum problem Problem Description Given a sequence 1,2,3,......N, your job is to calculate all the ...

随机推荐

  1. Python 多进程编程之 进程间的通信(Queue)

    Python 多进程编程之 进程间的通信(Queue) 1,进程间通信Process有时是需要通信的,操作系统提供了很多机制来实现进程之间的通信,而Queue就是其中的一个方法----这是操作系统开辟 ...

  2. Chapter3_操作符_算术操作符

    java中的算术操作符与其它语言并无太大区别,常用到的是以下这些: (1)加号(+),减号(-),除号(\),乘号(*),取模操作符(%),其中除号需要特别注意的是,会自动截取掉小数点后面的部分,而不 ...

  3. Python Day 3

    阅读目录: 内容回顾: 变量(标识符)的命名规范: 常量: 格式化输入\输出: 注释: 基本数据类型: 运算符: ##内容回顾 1.语言的分类: -- 机器语言:直接编写0,1指令,直接能被硬件执行. ...

  4. 关于webconfig的记录恢复本

    <?xml version="1.0"?> <!--注意: 除了手动编辑此文件以外,您还可以使用 Web 管理工具来配置应用程序的设置.可以使用 Visual S ...

  5. 2019.02.11 bzoj4818: [Sdoi2017]序列计数(矩阵快速幂优化dp)

    传送门 题意简述:问有多少长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数,且其中至少有一个数是质数,答案对201704082017040820170408取模(n≤1e9, ...

  6. explain 类型分析

    all 全表扫描 ☆ index 索引全扫描 ☆☆ range 索引范围扫描,常用语<,<=,>=,between等操作 ☆☆☆ ref 使用非唯一索引扫描或唯一索引前缀扫描,返回单 ...

  7. ETL概念详解

    ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程,目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供分析依据. ETL是BI项目重要的一个环节. 通常情况下,在 ...

  8. WPF常用样式总结

    常用控件样式: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation ...

  9. 迁移桌面程序到MS Store(5)——.NET Standard

    接下来的几篇,我想讨论下迁移桌面程序到MS Store,可以采用的比较常见.通用性比较强的实施步骤和分层架构. 通常商业项目一般都是不断的迭代,不太可能突然停止更新现有的桌面版本,然后花很长时间从头来 ...

  10. 建立一个类似于天眼的Android应用程序:第4部分 - 持久收集联系人,通话记录和短信(SMS)

    建立一个类似于天眼的Android应用程序:第4部分 - 持久收集联系人,通话记录和短信(SMS) 电话黑客android恶意软件编程黑客入侵linux 随着我们继续我们的系列,AMUNET应用程序变 ...