问题描述:
0-1背包问题:给定n种物品和一背包。物品 i 的重量似乎 w[i],其价值为 v[i],背包的容量为 c。"0-1"的意思就是,对于每一件物品必须选择取(用1表示)或者不取(用0表示),且每件物品只能被取一次。问如何选择物品,使得装入背包中物品的总价值最大?
这是数学问题中的线性规划问题,我们可以在线性约束范围内求解目标表达式。
具体算法可以参考:https://www.jianshu.com/p/a66d5ce49df5。
编程准备:
在窗体中添加相应控件。
首先看一下运行结果:
左侧listbox中的是所有的物品,编号,重量(weight),价格(Price)。我采用的是将其ini文件中,在读取出来,后期也方便修改,可以在程序里面修改,也能直接修改本地文件。
右侧的richTextBox中的是0-1背包问题的二维递归表,第一行从左往右是背包容量依次增加,第一列为物品编号,其余为背包在对应容量下的最大价值。
主要代码:
这里我自己定义的一个类 myItems,里面有属性:物品编号,重量,价格(都为int型),选中状态(bool型)。
mmyAllItems 是所有物品组成的一个列表。
mmyBagMax 是背包最大容量。
/// <summary>
/// 所有商品存放list
/// </summary>
public static List<myItems> mmyAllItems = new List<myItems>();
/// <summary>
/// 背包最大容量
/// </summary>
public static int mmyBagMax = 10;
主要算法实现
int[,] mainRect = new int[mmyAllItems.Count, mmyBagMax + 1];
for (int j = 1; j <= mmyBagMax; j++)
{
if (j >= mmyAllItems[0].weight)
{
mainRect[0, j] = mmyAllItems[0].price;
}
}
for (int i = 1; i < mmyAllItems.Count; i++)
{
for (int j = 1; j <= mmyBagMax; j++)
{
//不装入背包
if (j < mmyAllItems[i].weight)
{
mainRect[i, j] = mainRect[i - 1, j];
}
else
{
//选择价值较大者
if (mainRect[i - 1, j - mmyAllItems[i].weight] + mmyAllItems[i].price > mainRect[i - 1, j])
mainRect[i, j] = mainRect[i - 1, j - mmyAllItems[i].weight] + mmyAllItems[i].price;
else mainRect[i, j] = mainRect[i - 1, j];
}
}
}
for (int i = mmyAllItems.Count - 1; i >= 1; i--)
{
if (mainRect[i, mmyBagMax] > mainRect[i - 1, mmyBagMax])
{
mmyAllItems[i].IsChecked = true;
mmyBagMax -= mmyAllItems[i].weight; //物品i装入背包之前背包的容量
}
else
{
//没有装入背包
mmyAllItems[i].IsChecked = false;
}
}
//第一个物品
if(mmyBagMax!=0)
{
if (mainRect[0, mmyBagMax] != 0)
mmyAllItems[0].IsChecked = true;
else
mmyAllItems[0].IsChecked = false;
}
最后再根据mmyAllItems中的每个item的IsChecked属性来判断有没有选中,再进行相应的窗体显示。在这里需要注意跨线程调用控件这个问题,可以采用委托。
敬请各位指正。