/// <summary> /// 开始解决问题 /// </summary> /// <param name="testdata">要解决的问题的描述字符串</param> public override void Solve(string testdata) { if (this.UIReference == null) { throw new Exception("问题解决器尚未初始化就被使用"); } // 算法开始 this.BeginTimeStamp = DateTime.Now; // 分析测试数据字符串 this.testData = testdata; string[] lineitem = this.testData.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); this.Capacity = Convert.ToInt32(lineitem[0]); // 读取背包大小和物品种类数 this.ItemTypeCount = Convert.ToInt32(lineitem[1]); this.Items = new List <PackageItem>(); // 读取每种物品的属性 for (int i = 2; i < lineitem.Length; i++) { var aLine = lineitem[i].Split('\t'); var aW = Convert.ToInt32(aLine[1]); var aV = Convert.ToInt32(aLine[2]); this.Items.Add(new PackageItem((i - 2).ToString(), aW, aV)); } // 对单位价值做排序 this.Items.Sort((x, y) => { if (x.UnitValue > y.UnitValue) { return(-1); } else if (x.UnitValue < y.UnitValue) { return(1); } else { return(0); } }); // 初始化算法 this.currentMaxBound = 0; this.currentBestValue = 0; this.candidateRouterNode = null; BBNode recursiveNode = new BBNode(-1, null); // 调用回溯函数,开始递归求解 this.BackTrace(0, recursiveNode); // 反算路径 this.GetSolutionRouter(); // 算法结束 this.EndTimeStamp = DateTime.Now; }
/// <summary> /// 分支界限求最大价值路径 /// </summary> /// <returns>能够达到的最大价值</returns> private void BranchAndBound() { int itemId = 0; double currentUpperBound = this.GetMaxBound(itemId); this.heapQueue = new PriorityQueue <BBNode>(); BBNode currentExpandNode = null; // 分支界限搜索 while (true) { // 左子树(放入)可以拓展 double LeftWeight = this.currentWeight + this.Items[itemId].Weight; if (LeftWeight <= this.Capacity) { if (this.currentValue + this.Items[itemId].Value > this.currentBestValue) { this.currentBestValue = this.currentValue + this.Items[itemId].Value; this.CandidateRouterDestination = this.InsertToHeap(currentUpperBound, this.currentValue + this.Items[itemId].Value, this.currentWeight + this.Items[itemId].Weight, itemId + 1, true, currentExpandNode); } else { this.InsertToHeap(currentUpperBound, this.currentValue + this.Items[itemId].Value, this.currentWeight + this.Items[itemId].Weight, itemId + 1, true, currentExpandNode); } } currentUpperBound = this.GetMaxBound(itemId + 1); // 右子树的价值上界比当前最大值还大才有拓展的意义 if (currentUpperBound >= this.currentBestValue) { this.CandidateRouterDestination = this.InsertToHeap(currentUpperBound, this.currentValue, this.currentWeight, itemId + 1, false, currentExpandNode); } // 所有节点都已经展开就返回 if (this.heapQueue.Empty()) { return; } // 取下一个要生长的节点 currentExpandNode = this.heapQueue.Top(); this.heapQueue.Pop(); this.currentWeight = currentExpandNode.AccWeight; this.currentValue = currentExpandNode.AccValue; currentUpperBound = currentExpandNode.ValueUpperBound; itemId = currentExpandNode.Level; } }
/// <summary> /// 将一个新的活结点插入到子集树和最大堆heap中 /// </summary> /// <param name="maxValue">价值上界</param> /// <param name="accValue">节点累积价值</param> /// <param name="accWeight">节点累积质量</param> /// <param name="level">节点层次</param> /// <param name="pickFlag">是否挑选当前物品</param> /// <param name="parent">上层节点</param> /// <returns>加入的节点</returns> private BBNode InsertToHeap(double maxValue, double accValue, double accWeight, int level, bool pickFlag, BBNode parent) { BBNode node = new BBNode(level, null); node.ValueUpperBound = maxValue; node.AccValue = accValue; node.AccWeight = accWeight; node.Pick = pickFlag; node.Parent = parent; if (level <= this.ItemTypeCount) { this.heapQueue.Push(node); } return(node); }
/// <summary> /// 求子树的价值上界 /// </summary> /// <param name="itemId">当前考虑的物品</param> /// <returns>最大上界</returns> private double GetValueBound(int itemId, BBNode currentNode) { double space = this.Capacity - currentNode.AccWeight; double maxborder = currentNode.AccValue; // 要放入的所有物品应该比当前剩余空间还要小 while (itemId < this.ItemTypeCount && this.Items[itemId].Weight <= space) { space -= this.Items[itemId].Weight; maxborder += this.Items[itemId].Value; itemId++; } // 装填剩余容量装满背包 if (itemId < this.ItemTypeCount) { maxborder += ((double)this.Items[itemId].Value / (double)this.Items[itemId].Weight) * space; } return(maxborder); }
/// <summary> /// 回溯法搜索问题最优解 /// </summary> /// <param name="depth">当前递归的深度</param> /// <param name="parent">搜索树上层节点</param> private void BackTrace(int depth, BBNode parent) { // 递归边界 if (depth >= this.ItemTypeCount) { // 优于局部最小值时替换她 if (this.candidateRouterNode == null || parent.AccValue > this.candidateRouterNode.AccValue) { this.currentBestValue = parent.AccValue; this.candidateRouterNode = parent; } return; } // 计算价值上界 this.currentMaxBound = this.GetValueBound(depth, parent); // 如果下一物体可以放入就尝试放入 if (parent.AccWeight + this.Items[depth].Weight <= this.Capacity) { // 放入 BBNode leftNode = new BBNode(depth, parent); leftNode.Pick = true; leftNode.AccValue = parent.AccValue + this.Items[depth].Value; leftNode.AccWeight = parent.AccWeight + this.Items[depth].Weight; // 递归 this.BackTrace(depth + 1, leftNode); } // 不放入当前物品的情况 this.currentMaxBound = this.GetValueBound(depth + 1, parent); if (this.currentMaxBound >= this.currentBestValue) { BBNode rightNode = new BBNode(depth, parent); rightNode.AccValue = parent.AccValue; rightNode.AccWeight = parent.AccWeight; this.BackTrace(depth + 1, rightNode); } }