• 如何从一堆数中选出若干个数,使其和等于给定的数?


    如题,比如有一堆数:13,2,4,2,4,8,7,8,6
    要从中挑选出若干个数,使得它们的和等于32,挑选出来的数是:20,6,4,2
     
    我是使用“试探”法来解这个题目,思路如下:
     
    先对数进行排序:13,8,8,7,6,4,4,2,2
     
    选出最大的数字,以及不大于目标数字后续数字,于是我挑选到了13,8,8,其和是29,如果这个时候再挑选7的话就会超过32,所以就跳过,尝试在后面找到合适的数字,找到4,加上仍然大于32,再接着找到2,这次好了,加起来是31。
    再次向后面寻找小的数字的时候,发现没有合适的数字了。于是就“退回去”到最后一个选中的数字2那里,取消掉2的选择,选择下一个更小的数字:
    但不幸的是仍然不符合要求,而且已经到底了,所以还要往前退,退到8,取消对8的选择,选择更小的数字7:
    再尝试选择小于等于32的数字,6不符合,跳过,4,正好符合,13+8+7+4=32,挑选数字完成!
     
    好,算法描述好了,如何用代码来实现?
     
    这种不知道要循环多少次的问题最好还是用递归来处理,把这个问题简化成以下的问题:
    具体代码见下:
    public class CombineHelper {
            private readonly int[] _array;
            private readonly bool[] _chosenFlags;
            //排序,初始化
            public CombineHelper(IEnumerable<int> numbersToFetch) {
                Debug.Assert(numbersToFetch!=null);
                _array = numbersToFetch.OrderByDescending(i => i).ToArray();
                _chosenFlags = new bool[_array.Length];
            }
            //找组合
            public List<int> FindCombination(int value) {
                //初始化flags
                for (int i = 0; i < _chosenFlags.Length; i++) {
                    _chosenFlags[i] = false;
                }
                if (Find(value, 0)) {
                    //生成结果返回
                    List<int> result = new List<int>();
                    for (int i = 0; i < _chosenFlags.Length; i++) {
                        if (_chosenFlags[i]) {
                            result.Add(_array[i]);
                        }
                    }
                    return result;
                }
                throw new Exception("无法组合");
            }
            private bool Find(int value, int startIdx) {
                int i = startIdx;
                if (i == _array.Length) {
                    return false;
                }
                //跳过
                if (_array[i] > value) {
                    return Find(value, i + 1);
                }
                //匹配成功
                if (_array[i] == value) {
                    _chosenFlags[i] = true;
                    return true;
                }
                //_array[i] < value,尝试选择当前这个数并在后面的数中再去匹配余数
                for (int step = 0; i + step < _array.Length; step++) {
                    if (Find(value - _array[i + step], i + step + 1)) {
                        _chosenFlags[i + step] = true;
                        return true;
                    }
                }
                return false;
            }
        }

    这段代码除开一些封装/初始化的部分之外,也没几行了,真正有用的就是Find方法,递归的代码就是简洁。用法示例: 

        List<int> sourceList = new List<int> {13,2,4,2,4,8,7,8,6};
        CombineHelper combineHelper = new CombineHelper(sourceList);
        List<int> result = combineHelper.FindCombination(33);
        foreach (int i in result) {
            Console.WriteLine(i);
        }
  • 相关阅读:
    c copy
    IfcVertexLoop
    qt windeployqt 日志
    IfcPolyLoop
    IfcEdgeLoop
    IfcLoop
    QTableWidget
    QList删除元素
    matlab X的负次方函数绘制2
    matlab X的负次方函数绘制1
  • 原文地址:https://www.cnblogs.com/guogangj/p/8446861.html
Copyright © 2020-2023  润新知