本文乃Siliphen原创,转载请注明出处:http://blog.csdn.net/stevenkylelee
在实际项目中遇到一个问题。有一个总数N,和一个元素集合{ e1,e2 ...},求N可以是由元素集合中的什么元素相加组成。例如:总数49,元素集合{3,7,11},有如下解:
49 = 3 + 3 + 3 + 7 + 11 + 11 + 11
49 = 3 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 3 + 11 + 11
...
这个问题类似于现实生活中的找零钱,在一些网络棋牌游戏的断线重连恢复场景上有应用。
对于这个问题本人实现了一个算法,在此分享下。
把一个数分解若干个相加的集合元素,可以看作是对一棵树的搜索。
从根结点开始减元素集合中的元素,某个树节点的总数被减为0,那个树节点到树根结点的路径就是一个解。
示意图如下:
由于是对一棵树的搜索,可以用DFS也可以用BFS。算法步骤如下:
树节点携带2个值:
本节点表示的剩余总数
一个元素值,记录本节点由父节点减掉哪个元素生成的。
根结点的值是总数,算法初始时只有一个携带总数值的根结点,从这个根结点开始展开整棵树。
每个节点扩展子节点的规则是:
当前结点的总值减掉元素列表中的每个元素,
每次总数减去一个元素列表中的一个元素就生成一个子节点。
当一个节点的总值为0时,找到了一个解。
因为总数刚好是被元素集合中的元素减为0的,
通过这条树路径可以用一路减掉的元素加回到最初的根结点总数。
当一个节点的总值小于0时,表明这条树路径无解。
以上思路,我实现的程序如下(算法实现70行左右):
有一些解可以认为是重复的,比如
3 + 7 + 13 + 13 + 13
7 + 3 + 13 + 13 + 13
只是3和7调换了个位置。
这是因为这2个解对应的是2条不同的树路径。
如果元素集合中有1的话,就一定会有解,并且有相当多个解。
因为1是万能配,和自身或者其他任何元素相加都可以达到总数。
本程序的可执行文件下载:
http://download.csdn.net/detail/stevenkylelee/9878260
本程序的源代码下载(C#实现):
http://download.csdn.net/detail/stevenkylelee/9878261
下面的链接是我一个朋友的实现,C++,
比较简洁,核心算法20多行。
可去掉重复的版本