您的位置:首页 > 其它

背包分组问题的解法

2009-08-28 11:02 429 查看
背包分组问题的解法

作者:eaglet

今天在博问中看到这样一个问题按记录总值比例分组记录,这个问题本质上是一个背包分组的问题。eaglet花了2小时时间写了一个C#的实现,时间仓促,感觉还有很多值得改进的地方,不管怎么样,功能是实现了,贴出来给大家讨论吧。

我先把原题的意思按照我的理解再描述一遍:

有数组A假设为int[]goods={25,15,10,3,1,5,14,16,5,6};

我们希望将这些goods按下面给出的分组规则来分组。

我们有数组B假设为int[]sizes={50,30,20};我们希望把数组A分成三组,并且使每组的和与数组B对应的值最匹配。

本题的答案是

Group1:{10,3,1,14,16,6}

Group2:{25,5}

Group3:{15,5}

数组长度小的时候,用手算就可以分组,但如果长度大,分组数量多,则手算就很难了,需要寻求计算机的帮助。

我的解决思路是:

第一步用整个的goods数组分别按50,30,20计算最优组合,得到三组最优组合(注意这时这三组组合很可能有重复的记录)

第二步从这三组组合中取出最优的一组,也就是总和和对应的大小之差最小的一组,保留这组记录。

第三步从goods数组中将刚刚选中的那组数据剔除掉,然后用新的goods数组重复第一步,运算时不再运算已选出的组合,直到全部匹配或者只剩下最后一组。

第四步如果还剩下最后一组,则把剩余的goods全部给这一组,并输出。

下面给出代码

///<summary>
///背包分组
///</summary>
publicclassBackpackGroup
{
///<summary>
///找到最匹配的那个组别
///</summary>
///<paramname="sizes"></param>
///<paramname="result"></param>
///<returns></returns>
privateintGetMostMatchedIndex(int[]sizes,List<int>[]result)
{
intmin=int.MaxValue;
intindex=-1;
for(inti=0;i<sizes.Length;i++)
{
if(result[i]!=null)
{
intsum=0;
foreach(intvalueinresult[i])
{
sum+=value;
}
if(min>=sizes[i]-sum)
{
index=i;
min=sizes[i]-sum;
}
}
}
returnindex;
}
///<summary>
///得到剩余的goods
///</summary>
///<paramname="select"></param>
///<paramname="goods"></param>
///<returns></returns>
privateint[]GetLeftGoods(List<int>select,int[]goods)
{
List<int>result=newList<int>();
int?[]tempSelect=newint?[select.Count];
for(inti=0;i<select.Count;i++)
{
tempSelect[i]=select[i];
}
foreach(intvalueingoods)
{
boolthrowaway=false;
for(inti=0;i<select.Count;i++)
{
if(tempSelect[i]==null)
{
continue;
}
if(tempSelect[i]==value)
{
throwaway=true;
tempSelect[i]=null;
break;
}
}
if(!throwaway)
{
result.Add(value);
}
}
returnresult.ToArray();
}
///<summary>
///递归方式内部分组
///</summary>
///<paramname="goods"></param>
///<paramname="sizes"></param>
///<paramname="result"></param>
privatevoidInnerGroup(int[]goods,int[]sizes,List<int>[]result)
{
List<int>[]temp=newList<int>[result.Length];
result.CopyTo(temp,0);
for(inti=0;i<sizes.Length;i++)
{
if(temp[i]==null)
{
Backpackbackpack=newBackpack();
temp[i]=backpack.Match(goods,sizes[i]);
}
else
{
temp[i]=null;
}
}
intindex=GetMostMatchedIndex(sizes,temp);
if(index<0)
{
return;
}
result[index]=temp[index];
goods=GetLeftGoods(temp[index],goods);
intleft=0;
intlastIndex=-1;
for(inti=0;i<result.Length;i++)
{
if(result[i]==null)
{
lastIndex=i;
left++;
}
}
if(left==1)
{
result[lastIndex]=newList<int>(goods);
return;
}
InnerGroup(goods,sizes,result);
}
publicList<int>[]Group(int[]goods,int[]sizes)
{
List<int>[]result=newList<int>[sizes.Length];
InnerGroup(goods,sizes,result);
returnresult;
}
}
///<summary>
///背包算法
///</summary>
publicclassBackpack
{
privateList<int>_MatchGoods=newList<int>();
privateList<int>_TmpMatchGoods=newList<int>();
privateint[]_Goods;
privateint_Size;
privateint_Max=0;
privateboolfindMatch=false;
privateint_PreSum=0;
privatebool_CatchLast=false;
privatevoidInit(int[]goods,intsize)
{
_MatchGoods=newList<int>();
_TmpMatchGoods=newList<int>();
_Goods=goods;
_Size=size;
_Max=0;
findMatch=false;
_PreSum=0;
_CatchLast=false;
}
///<summary>
///递归计算从第start个元素开始的之后的最匹配结果
///</summary>
///<paramname="start"></param>
///<paramname="floor"></param>
privatevoidMatch(intstart,intfloor)
{
if(start>=_Goods.Length)
{
_CatchLast=true;
return;
}
if(_PreSum+_Goods[start]>_Size)
{
return;
}
_PreSum+=_Goods[start];
_TmpMatchGoods.Add(_Goods[start]);
if(start+1>=_Goods.Length)
{
_CatchLast=true;
return;
}
for(inti=start+1;i<_Goods.Length;i++)
{
Match(i,floor+1);
if(floor==0)
{
if(_PreSum==_Size)
{
findMatch=true;
_MatchGoods=_TmpMatchGoods;
}
else
{
if(_Max<_PreSum)
{
_Max=_PreSum;
_MatchGoods=_TmpMatchGoods;
}
}
if(_CatchLast)
{
return;
}
_TmpMatchGoods=newList<int>(_Goods.Length);
_PreSum=0;
_PreSum+=_Goods[start];
_TmpMatchGoods.Add(_Goods[start]);
}
}
}
publicList<int>Match(int[]goods,intsize)
{
Init(goods,size);
//以此计算各个元素的组合,找到第一个最匹配的结果
for(inti=0;i<goods.Length;i++)
{
_PreSum=0;
_CatchLast=false;
_TmpMatchGoods=newList<int>(_Goods.Length);
Match(i,0);
if(findMatch)
{
return_MatchGoods;
}
}
return_MatchGoods;
}
}

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

这个算法有个问题,就是背包算法只给出了一组最匹配的记录,如果最匹配的记录有多个(并列的),则情况会更复杂一些,不过这个相对简单的算法最后分组的效果已经不错。

另外背包算法感觉写的并不简洁,应该还有更好的写法。

测试代码

Backpackbackpack=newBackpack();
int[]goods={25,15,10,3,1,5,14,16,5,6};
int[]sizes={35,45,20};
BackpackGroupbackGroup=newBackpackGroup();
List<int>[]groups=backGroup.Group(goods,sizes);
foreach(List<int>matchGoodsingroups)
{
foreach(intmginmatchGoods)
{
Console.Write(mg);
Console.Write(",");
}
Console.WriteLine();
}
Console.ReadKey();

.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}

下面给出几个不同的分组的结果

int[]sizes={50,30,20};

10,3,1,14,16,6,
25,5,
15,5,

int[]sizes={70,10,20};

25,3,1,14,16,5,6,
10,
15,5,

int[]sizes={35,45,20};

10,3,1,16,6,
25,14,5,
15,5,

前两组完全匹配,最后一组近似匹配。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: