动态规划求解背包问题

背包问题

一堆物品,每件物品有对应的重量和价值。有一个固定容量的背包,随便装物品,如何让背包装的东西价值最大?

问题分析

背包中物品价值最大时,可能存在以下几种情况:

  1. 背包正好装了一件足够大的物品,物品价值比分开装其他物品的总价值还高;
  2. 背包空间可以分成A和B两部分,空间A和B都最大化利用,装价值最大的物品。但一种拆分方案没法保证合理拆分,必须遍历多种拆分方案;

因此,背包为题等效为对比情况1和情况2中最大的价值;情况2则可以拆分成两个背包问题的子问题,递归的情况下,可以继续不断拆分,直到无法继续拆分为止。问题拆分求解过程中,有些问题必然会重复出现。为了避免重复求解,还需要对已经求解过的问题的答案进行记录。

流程图

挑选物品流程图如下,粉丝方框表示迭代。

开始
输入数据
是否存在结果记录?
索引结果并返回
结束
空间容量是否小于最小物品重量?
返回空列表
挑选容量范围内价值最大物品,物品价值设为最值
遍历拆分方案
空间拆分
空间A挑选物品,添加到结果记录
空间B挑选剩余物品,添加到结果记录
空间A和B物品汇总,并计算总价值
总价值超过最值
更新最值和结果列表
遍历结束
添加到结果记录

代码实现

用c#来实现,主要代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PackageProblem
{
    public class DynamicProgramming
    {
        public List<Goods> GoodsForChose { get; set; }
        public int TotalWeightLimit { get; set; }
        public List<Goods> GoodsSelectd { get; set; }
        public int GoodsTotalWeight { get; set; }
        public int GoodsTotalValue { get; set; }

        private List<Tuple<int, List<int>, List<int>>> _tuplesRecord;

        public DynamicProgramming(int totalWeightLimit, List<Goods> goodsForSelect)
        {
            TotalWeightLimit = totalWeightLimit;
            GoodsForChose = goodsForSelect;
            _tuplesRecord = new List<Tuple<int, List<int>, List<int>>>();
        }

        public void SelectGoods()
        {
            var toSelectList = SelectGoods(TotalWeightLimit, GoodsForChose.Select((x, i) => i).ToList());
            GoodsSelectd = toSelectList.Select(x => GoodsForChose[x]).ToList();
            GoodsTotalWeight = GoodsSelectd.Select(x => x.Weight).Sum();
            GoodsTotalValue = GoodsSelectd.Select(x => x.Value).Sum();
        }

        private List<int> SelectGoods(int totalWeightLimit, List<int> goodsIndexForSelect)
        {
            var exitTuple = _tuplesRecord.Where(x => x.Item1 == totalWeightLimit && ListIntEqual(x.Item2, goodsIndexForSelect)).FirstOrDefault();
            if (exitTuple != null) return exitTuple.Item3;
            if (totalWeightLimit < goodsIndexForSelect.Select(x => GoodsForChose[x].Weight).Min()) return new List<int>();

            var oneGoods = SelectOneGoods(totalWeightLimit, goodsIndexForSelect);
            //if (oneGoods < 0) return new List<int>();

            var totalValue = GoodsForChose[oneGoods].Value;
            var totalGroup = new List<int> { oneGoods };
            for (var i = 1; i <= totalWeightLimit / 2; i++)
            {
                var partA = SelectGoods(i, goodsIndexForSelect);
                var tempTuple1 = new Tuple<int, List<int>, List<int>>(i, goodsIndexForSelect, partA);
                _tuplesRecord = AddTupleAndDistinct(_tuplesRecord, tempTuple1);

                var partB = SelectGoods(totalWeightLimit - i, goodsIndexForSelect.Except(partA).ToList());
                var tempTuple2 = new Tuple<int, List<int>, List<int>>(i, goodsIndexForSelect, partA);
                _tuplesRecord = AddTupleAndDistinct(_tuplesRecord, tempTuple2);

                var tempGroup = partA.ToList();
                tempGroup.AddRange(partB);
                var tempTotalValue = tempGroup.Select(x => GoodsForChose[x].Value).Sum();
                if (tempTotalValue > totalValue) { totalValue = tempTotalValue; totalGroup = tempGroup; }
            }
            _tuplesRecord = AddTupleAndDistinct(_tuplesRecord, new Tuple<int, List<int>, List<int>>(totalWeightLimit, goodsIndexForSelect, totalGroup));
            return totalGroup;
        }

        private bool ListIntEqual(List<int> listA, List<int> listB)
        {
            var result = true;
            if (listA.Count != listB.Count) return false;
            listA.ForEach(x => result = result && listB.Contains(x));
            return result;
        }

        private List<Tuple<int, List<int>, List<int>>> AddTupleAndDistinct(List<Tuple<int, List<int>, List<int>>> listTuple, Tuple<int, List<int>, List<int>> tuple)
        {
            var exitSameTuple = listTuple.Where(x => x.Item1 == tuple.Item1 && ListIntEqual(x.Item2, tuple.Item2)).Count() > 0;
            if (!exitSameTuple) listTuple.Add(tuple);
            return listTuple;
        }

        private int SelectOneGoods(int totalWeightLimit, List<int> goodsIndexForSelect)
        {
            var targetGoods = goodsIndexForSelect.Where(x => GoodsForChose[x].Weight <= totalWeightLimit);
            if (targetGoods.Count() == 0) return -1;
            return targetGoods.FirstOrDefault(x => GoodsForChose[x].Value == targetGoods.Select(y => GoodsForChose[y].Value).Max());
        }
    }

    public class Goods
    {
        public string Name { get; set; }
        public int Weight { get; set; }
        public int Value { get; set; }
    }
}

实现效果

为了调用上面的方法,做了个界面并准备相关数据,运行结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值