背包算法,C#
穷举法,这个最好理解了,省得死那么多脑细胞
/// <summary>
/// KnapSack 背包问题。
/// </summary>
public class KnapSack
{
/// <summary>
/// 可放入背包的物件
/// </summary>
public interface PackItem
{
/// <summary>
/// 可比对的值,统一转为decimal
/// </summary>
decimal Value { get; }
}
/// <summary>
/// 用来做位屏蔽的数组
/// </summary>
private int[] mask = null;
public KnapSack()
{
}
/// <summary>
/// 穷举
/// </summary>
/// <param name="array">PackItem对象数组</param>
/// <param name="upperLimit">比对上限</param>
/// <param name="resultSum">最后的总和结果</param>
/// <returns>被挑选对象的数组</returns>
public PackItem[] EndList(PackItem[] array,decimal upperLimit,ref decimal resultSum)
{
initMask(array.Length); //初始化屏蔽数组
int spaceNum = GetSpaceNum(array.Length); //穷举数
int plan = 0; //选择的方案号
decimal max = 0; //能达到的最大值
decimal sum = 0; //阶段性统计数
for(int i=1;i <= spaceNum;i++) //穷举第 i 种情况
{
sum = 0; //开始一种新情况,阶段性统计数归零
for(int j=0; j < array.Length; j++) //处理每个物件
{
if((i & mask[j]) == mask[j]) //在本方案(i)中,第 j 个物件是否要选择
{
sum += array[j].Value; //选择第j个物件,价值加到阶段性统计数上
}
}
if( sum > max && sum <= upperLimit) //是否满足条件?
{
max = sum; //满足条件,设定最新的最大值
plan = i; //记录下来这是第几个方案
}
}
resultSum = max; //结果
IList result = new ArrayList(); //把选中的方案的物件挑出来
for(int i=0;i < array.Length;i++)
{
if((plan & mask[i]) == mask[i]) //第i个物件是否选中了。
{
result.Add(array[i]); //选中了,加入到结果列表
}
}
PackItem[] resultArray = new PackItem[result.Count]; //放到数组里面
result.CopyTo(resultArray,0);
return resultArray; //返回
}
/// <summary>
/// 初始化 mask
/// </summary>
/// <param name="arrayLen"></param>
private void initMask(int arrayLen)
{
mask = new int[arrayLen];
for(int i=0;i < arrayLen;i++)
{
mask[i] = (1 << i); //左移
}
}
/// <summary>
/// 取得组合总数,高中数学忘记了吧,复习吧
/// </summary>
/// <param name="upper"></param>
/// <returns></returns>
private int GetSpaceNum(int upper)
{
int nUpper = GetSeq(upper); //n!
int c = 0;
for(int i=1; i <= upper; i++) //C(n,r) + C(n,r-1) + + C(n,1)
{
int rs = GetSeq(i);
int n2 = GetSeq(upper - i);
if(n2 == 0)
c++;
else
c += nUpper / (rs * n2); //C(n,r) = n! / ( (n-i)! * i! )
}
return c;
}
/// <summary>
/// 阶乘的递归算法
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
private int GetSeq(int n)
{
if(n > 1)
return n * GetSeq(n-1);
return 1;
}
测试
/// KnapSack 背包问题。
/// </summary>
public class KnapSack
{
/// <summary>
/// 可放入背包的物件
/// </summary>
public interface PackItem
{
/// <summary>
/// 可比对的值,统一转为decimal
/// </summary>
decimal Value { get; }
}
/// <summary>
/// 用来做位屏蔽的数组
/// </summary>
private int[] mask = null;
public KnapSack()
{
}
/// <summary>
/// 穷举
/// </summary>
/// <param name="array">PackItem对象数组</param>
/// <param name="upperLimit">比对上限</param>
/// <param name="resultSum">最后的总和结果</param>
/// <returns>被挑选对象的数组</returns>
public PackItem[] EndList(PackItem[] array,decimal upperLimit,ref decimal resultSum)
{
initMask(array.Length); //初始化屏蔽数组
int spaceNum = GetSpaceNum(array.Length); //穷举数
int plan = 0; //选择的方案号
decimal max = 0; //能达到的最大值
decimal sum = 0; //阶段性统计数
for(int i=1;i <= spaceNum;i++) //穷举第 i 种情况
{
sum = 0; //开始一种新情况,阶段性统计数归零
for(int j=0; j < array.Length; j++) //处理每个物件
{
if((i & mask[j]) == mask[j]) //在本方案(i)中,第 j 个物件是否要选择
{
sum += array[j].Value; //选择第j个物件,价值加到阶段性统计数上
}
}
if( sum > max && sum <= upperLimit) //是否满足条件?
{
max = sum; //满足条件,设定最新的最大值
plan = i; //记录下来这是第几个方案
}
}
resultSum = max; //结果
IList result = new ArrayList(); //把选中的方案的物件挑出来
for(int i=0;i < array.Length;i++)
{
if((plan & mask[i]) == mask[i]) //第i个物件是否选中了。
{
result.Add(array[i]); //选中了,加入到结果列表
}
}
PackItem[] resultArray = new PackItem[result.Count]; //放到数组里面
result.CopyTo(resultArray,0);
return resultArray; //返回
}
/// <summary>
/// 初始化 mask
/// </summary>
/// <param name="arrayLen"></param>
private void initMask(int arrayLen)
{
mask = new int[arrayLen];
for(int i=0;i < arrayLen;i++)
{
mask[i] = (1 << i); //左移
}
}
/// <summary>
/// 取得组合总数,高中数学忘记了吧,复习吧
/// </summary>
/// <param name="upper"></param>
/// <returns></returns>
private int GetSpaceNum(int upper)
{
int nUpper = GetSeq(upper); //n!
int c = 0;
for(int i=1; i <= upper; i++) //C(n,r) + C(n,r-1) + + C(n,1)
{
int rs = GetSeq(i);
int n2 = GetSeq(upper - i);
if(n2 == 0)
c++;
else
c += nUpper / (rs * n2); //C(n,r) = n! / ( (n-i)! * i! )
}
return c;
}
/// <summary>
/// 阶乘的递归算法
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
private int GetSeq(int n)
{
if(n > 1)
return n * GetSeq(n-1);
return 1;
}
测试
这个算法最多不能超过32个物件。