背包分组问题的解法

 

背包分组问题的解法

作者:eaglet

     今天在博问中看到这样一个问题 按记录总值比例分组记录 ,这个问题本质上是一个背包分组的问题。eaglet 花了2小时时间写了一个C#的实现,时间仓促,感觉还有很多值得改进的地方,不管怎么样,功能是实现了,贴出来给大家讨论吧。

     我先把原题的意思按照我的理解再描述一遍:

     有数组A 假设为 int[] goods = {25,15,10,3,1, 5, 14, 16, 5, 6};

     我们希望将这些goods 按下面给出的分组规则来分组。

     我们有数组B 假设为 int[] sizes = {50, 30, 20}; 我们希望把数组A分成三组,并且使每组的和与数组B对应的值最匹配。

     本题的答案是

     Group1 : {10,3,1,14,16,6}

     Group2 : {25,5}

     Group3 : {15,5}   

     数组长度小的时候,用手算就可以分组,但如果长度大,分组数量多,则手算就很难了,需要寻求计算机的帮助。

     我的解决思路是:

     第一步用整个的goods 数组分别按 50, 30 ,20 计算最优组合,得到三组最优组合(注意这时这三组组合很可能有重复的记录)

     第二步从这三组组合中取出最优的一组,也就是总和和对应的大小之差最小的一组,保留这组记录。

     第三步从goods数组中将刚刚选中的那组数据剔除掉,然后用新的goods 数组重复第一步,运算时不再运算已选出的组合,直到全部匹配或者只剩下最后一组。

     第四步如果还剩下最后一组,则把剩余的goods 全部给这一组,并输出。

 

     下面给出代码

    /// <summary>
    /// 背包分组
    /// </summary>
    public
 class
 BackpackGroup
    {
        /// <summary>
        /// 找到最匹配的那个组别
        /// </summary>
        /// <param name="sizes"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        private
 int
 GetMostMatchedIndex(int
[] sizes, List<int
>[] result)
        {
            int
 min = int
.MaxValue;
            int
 index = -1;
 
            for
 (int
 i = 0; i < sizes.Length; i++)
            {
                if
 (result[i] != null
)
                {
                    int
 sum = 0;
                    foreach
 (int
 value
 in
 result[i])
                    {
                        sum += value
;
                    }
 
                    if
 (min >= sizes[i] - sum)
                    {
                        index = i;
                        min = sizes[i] - sum;
                    }
                }
            }
 
            return
 index;
        }
 
        /// <summary>
        /// 得到剩余的goods
        /// </summary>
        /// <param name="select"></param>
        /// <param name="goods"></param>
        /// <returns></returns>
        private
 int
[] GetLeftGoods(List<int
> select, int
[] goods)
        {
            List<int
> result = new
 List<int
>();
 
            int
?[] tempSelect = new
 int
?[select.Count];
 
            for
 (int
 i = 0; i < select.Count; i++)
            {
                tempSelect[i] = select[i];
            }
 
            foreach
 (int
 value
 in
 goods)
            {
                bool
 throwaway = false
;
 
                for
 (int
 i = 0; i < select.Count; i++)
                {
                    if
 (tempSelect[i] == null
)
                    {
                        continue
;
                    }
 
                    if
 (tempSelect[i] == value
)
                    {
                        throwaway = true
;
                        tempSelect[i] = null
;
                        break
;
                    }
                }
 
                if
 (!throwaway)
                {
                    result.Add(value
);
                }
            }
 
            return
 result.ToArray();
        }
 
        /// <summary>
        /// 递归方式内部分组
        /// </summary>
        /// <param name="goods"></param>
        /// <param name="sizes"></param>
        /// <param name="result"></param>
        private
 void
 InnerGroup(int
[] goods, int
[] sizes, List<int
>[] result)
        {
            List<int
>[] temp = new
 List<int
>[result.Length];
            result.CopyTo(temp, 0);
 
            for
 (int
 i = 0; i < sizes.Length; i++)
            {
                if
 (temp[i] == null
)
                {
                    Backpack backpack = new
 Backpack();
                    temp[i] = backpack.Match(goods, sizes[i]);
                }
                else
                {
                    temp[i] = null
;
                }
            }
 
            int
 index = GetMostMatchedIndex(sizes, temp);
 
            if
 (index < 0)
            {
                return
;
            }
 
            result[index] = temp[index];
 
            goods = GetLeftGoods(temp[index], goods);
 
            int
 left = 0;
            int
 lastIndex = -1;
            
            for
(int
 i = 0; i < result.Length; i++)
            {
                if
 (result[i] == null
)
                {
                    lastIndex = i;
                    left++;
                }
            }
 
            if
 (left == 1)
            {
                result[lastIndex] = new
 List<int
>(goods);
                return
;
            }
 
 
            InnerGroup(goods, sizes, result);
        }
 
        public
 List<int
>[] Group(int
[] goods, int
[] sizes)
        {
            List<int
>[] result = new
 List<int
>[sizes.Length];
            InnerGroup(goods, sizes, result);
            return
 result;
        }
    }
 
 
    /// <summary>
    /// 背包算法
    /// </summary>
    public
 class
 Backpack
    {
        private
 List<int
> _MatchGoods = new
 List<int
>();
        private
 List<int
> _TmpMatchGoods = new
 List<int
>();
        private
 int
[] _Goods;
        private
 int
 _Size;
        private
 int
 _Max = 0;
        private
 bool
 findMatch = false
;
        private
 int
 _PreSum = 0;
        private
 bool
 _CatchLast = false
;
 
        private
 void
 Init(int
[] goods, int
 size)
        {
            _MatchGoods = new
 List<int
>();
            _TmpMatchGoods = new
 List<int
>();
            _Goods = goods;
            _Size = size;
            _Max = 0;
            findMatch = false
;
            _PreSum = 0;
            _CatchLast = false
;
        }
 
        /// <summary>
        /// 递归计算从第start个元素开始的之后的最匹配结果
        /// </summary>
        /// <param name="start"></param>
        /// <param name="floor"></param>
        private
 void
 Match(int
 start, int
 floor)
        {
            if
 (start >= _Goods.Length)
            {
                _CatchLast = true
;
                return
;
            }
 
            if
 (_PreSum + _Goods[start] > _Size)
            {
                return
;
            }
 
            _PreSum += _Goods[start];
 
            _TmpMatchGoods.Add(_Goods[start]);
 
            if
 (start + 1 >= _Goods.Length)
            {
                _CatchLast = true
;
                return
;
            }
 
            for
 (int
 i = start + 1; i < _Goods.Length; i++)
            {
                Match(i, floor + 1);
 
                if
 (floor == 0)
                {
                    if
 (_PreSum == _Size)
                    {
                        findMatch = true
;
                        _MatchGoods = _TmpMatchGoods;
                    }
                    else
                    {
                        if
 (_Max < _PreSum)
                        {
                            _Max = _PreSum;
                            _MatchGoods = _TmpMatchGoods;
                        }
                    }
 
                    if
 (_CatchLast)
                    {
                        return
;
                    }
 
                    _TmpMatchGoods = new
 List<int
>(_Goods.Length);
                    _PreSum = 0;
 
                    _PreSum += _Goods[start];
                    _TmpMatchGoods.Add(_Goods[start]);
                }
            }
        }
 
        public
 List<int
> Match(int
[] goods, int
 size)
        {
            Init(goods, size);
 
            //以此计算各个元素的组合,找到第一个最匹配的结果
            for
 (int
 i = 0; i < goods.Length; i++)
            {
                _PreSum = 0;
                _CatchLast = false
;
                _TmpMatchGoods = new
 List<int
>(_Goods.Length);
 
                Match(i, 0);
 
                if
 (findMatch)
                {
                    return
 _MatchGoods;
                }
            }
 
            return
 _MatchGoods;
        }
    }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

这个算法有个问题,就是背包算法只给出了一组最匹配的记录,如果最匹配的记录有多个(并列的),则情况会更复杂一些,不过这个相对简单的算法最后分组的效果已经不错。

另外背包算法感觉写的并不简洁,应该还有更好的写法。

测试代码

            Backpack backpack = new
 Backpack();
 
            int
[] goods = {25,15,10,3,1, 5, 14, 16, 5, 6};
            int
[] sizes = {35, 45, 20};
            BackpackGroup backGroup = new
 BackpackGroup();
            List<int
>[] groups = backGroup.Group(goods, sizes);
 
            foreach
 (List<int
> matchGoods in
 groups)
            {
                foreach
 (int
 mg in
 matchGoods)
                {
                    Console.Write(mg);
                    Console.Write(","
);
                }
                Console.WriteLine();
            }
            Console.ReadKey();
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

下面给出几个不同的分组的结果

int[] sizes = {50, 30, 20};

10,3,1,14,16,6,
25,5,
15,5,

 

int[] sizes = {70, 10, 20};

25,3,1,14,16,5,6,
10,
15,5,

 

int[] sizes = {35, 45, 20};

10,3,1,16,6,
25,14,5,
15,5,

前两组完全匹配,最后一组近似匹配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值