using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace KnapsackProblem { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { int KnapsackWeight = 10; List<AvailableInfo> Availables = new List<AvailableInfo>(); Availables.Add(new AvailableInfo() { Weight = 2, Value = 6 }); Availables.Add(new AvailableInfo() { Weight = 2, Value = 3 }); Availables.Add(new AvailableInfo() { Weight = 6, Value = 5 }); Availables.Add(new AvailableInfo() { Weight = 5, Value = 4 }); Availables.Add(new AvailableInfo() { Weight = 4, Value = 6 }); int result = DivideandConquer(Availables, 0, KnapsackWeight); richTextBox1.Text = result.ToString() + Environment.NewLine; } /// <summary> /// 分治法 /// </summary> /// <param name="availables">物品列表</param> /// <param name="index">当前分配索引</param> /// <param name="availableWeight">当前可用重量</param> /// <returns></returns> private int DivideandConquer(List<AvailableInfo> availables, int index, int availableWeight) { int result = 0; if (index >= availables.Count || availableWeight <= 0) { return 0;//如果索引超出物品列表或者可用重量小于0 } else if (availables[index].Weight > availableWeight) { //如果装不下,且直接计算下一个物品 result = DivideandConquer(availables, index + 1, availableWeight); } else { // 可以放下,比较放入当前物品和不放入当前物品的结果,取价值大的一方 result = Math.Max(DivideandConquer(availables, index + 1, availableWeight - availables[index].Weight) + availables[index].Value, DivideandConquer(availables, index + 1, availableWeight)); } return result; } } public class AvailableInfo { public AvailableInfo() { } /// <summary> /// 物品重量 /// </summary> public int Weight { get; set; } /// <summary> /// 物品价值 /// </summary> public int Value { get; set; } } }
如果你希望在解决背包问题的同时获得放入背包的所有物品列表,你可以对分治法进行一些修改,以记录所选择的物品。在这里,我对你提供的代码进行了修改,添加了一个用于存储选择的物品的列表 selectedItems,以及一个用于记录当前最优解的列表 bestItems。以下是修改后的代码:
csharp
// 在 Form1 类中添加两个列表
List<AvailableInfo> selectedItems = new List<AvailableInfo>();
List<AvailableInfo> bestItems = new List<AvailableInfo>();
// button1_Click 方法
private void button1_Click(object sender, EventArgs e)
{
int KnapsackWeight = 10;
List<AvailableInfo> Availables = new List<AvailableInfo>();
Availables.Add(new AvailableInfo() { Weight = 2, Value = 6 });
Availables.Add(new AvailableInfo() { Weight = 2, Value = 3 });
Availables.Add(new AvailableInfo() { Weight = 6, Value = 5 });
Availables.Add(new AvailableInfo() { Weight = 5, Value = 4 });
Availables.Add(new AvailableInfo() { Weight = 4, Value = 6 });
// 重置列表
selectedItems.Clear();
bestItems.Clear();
int result = DivideandConquer(Availables, 0, KnapsackWeight);
// 将选择的物品显示在 RichTextBox 中
richTextBox1.Text = "最大价值: " + result.ToString() + Environment.NewLine;
richTextBox1.Text += "选择的物品列表:" + Environment.NewLine;
foreach (var item in bestItems)
{
richTextBox1.Text += $"Weight: {item.Weight}, Value: {item.Value}" + Environment.NewLine;
}
}
// 修改 DivideandConquer 方法,记录选择的物品
private int DivideandConquer(List<AvailableInfo> availables, int index, int availableWeight)
{
int result = 0;
if (index >= availables.Count || availableWeight <= 0)
{
return 0;
}
else if (availables[index].Weight > availableWeight)
{
result = DivideandConquer(availables, index + 1, availableWeight);
}
else
{
// 在两种情况中选择总价值更大的一方,并记录选择的物品列表
int withCurrentItem = DivideandConquer(availables, index + 1, availableWeight - availables[index].Weight) + availables[index].Value;
int withoutCurrentItem = DivideandConquer(availables, index + 1, availableWeight);
if (withCurrentItem > withoutCurrentItem)
{
// 选择当前物品
selectedItems.Add(availables[index]);
result = withCurrentItem;
}
else
{
// 不选择当前物品
result = withoutCurrentItem;
}
}
// 更新最优解的物品列表
bestItems.Clear();
bestItems.AddRange(selectedItems);
return result;
}
上述修改中,我添加了 selectedItems 用于存储当前选择的物品列表,以及 bestItems 用于存储当前最优解的物品列表。在递归过程中,根据选择情况更新这两个列表。在最终显示结果时,将 bestItems 中的物品列表显示在 RichTextBox 中。
感谢回复。
这几天我也一直在用类似的方法在寻求答案,最终用类似的方式找到了解决的途径。
你给出的办法,一旦物品清单比较大时,bestItems里的数据会有大量的重复数据,给回溯增加了难度。
以下是我的解决办法:
和你的思路一样,每次出现"选择当前物品"时,也记录被选择的物品,但同时还在记录index参数。
得到最优解后,将最终的Value放入bestItems,按Index顺序回溯。直到Value被回归到0值。
依然非常感觉。