背包问题的一些想法

原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。

由于各种原因,可能存在诸多不足,欢迎斧正!    

在此说明,以下是我算法基础的节课报告,彭雷老师要求以组为单位做汇报,汇报已过,贴出思路与大家共享,并作为读书笔记存于博客。

问题描述

        背包问题有很多种,一般描述为有一背包容量为maxVolume,有numOfGood件物品,对应体积、价值和数量分别为volume[i]、price[i]、number[i]。

       满足以下方程

                ∑volume[i]*tempNumber[i]<=maxVolume

                tempNumber[i]<=number[i]

      使总价值:

              sumPrice=∑price[i]*tempNumber[i]

        sumPrice最大。

 

关于背包问题,有很多问题值得思考。

1、物品是否可分

    (1)、若每件物品可分,即分成任意小的物品,则问题简化很多,依次直接取价值/体积大的,直到背包装满或者物品用完为止,此种情况下sumPrice最大。

    (2)、若每件物品不可分,则无法通过上述贪心策略解决,这是我们组要解决的。

 

2、背包是否装满

      背包装不装满对问题的求解起到至关重要的作用。比如:背包容积为maxVolume=4,物品numOfGood=3种,分别(体积,价值,数量)为Good[]={(1,10,1)、(2,20,1)、(4,1,1)}

      在背包装满的情况下为选物品1,总价值为1;

      若不要求未满,则可取物品2+物品3,总体积为1+2=3<4,总价值为10+20=30.

 

3、物品数量

       根据物品数量,大致可分3种

       (1)、Number[i]=1          0<=i<numOfGood

       (2)、Number[i]=∞         0<=i<numOfGood

       (3)、Number[i]=const       0<=i<numOfGood


       分别对应01背包、完全背包、多重背包。

 

4、解的准确程度

        问题的规模不同,如背包容积maxVolume,物品数量numOfGood会影响得到最终解所消耗的时间、空间资源,即影响程序的效率。效率与解的准确程度往往是矛盾的,我们需要在二者之间进行折中。于是我们尝试了多种方法:搜索法与动态规划法求最优解,遗传算法与贪心算法求解可行的近似解。

 

 

解决方案

     依据物品数量,可以分为01背包、完全背包、多重背包。在此我们分别给出不同的解法。

一、01背包

     依据背包容积maxVolume与物品数量numOfGood的数量级,我们给出了4类解法。

1. 枚举法  

      当物品数量numOfGood较小的时候,可以尝试物品枚举子集,找出对应价值最大者。关于枚举,我们尝试了两种不同的方法:位操作枚举、搜索枚举。

1.1位操作枚举法

      此种方法适合物品数量numOfGood较小,一般满足numOfGood<=15,总的子集数cnt=2^numOfGood,然后就是依据位操作的特点寻找符合条件的解。下面给出伪代码:

for(子集状态i从0到cnt)

{

         for(枚举每件物品i)

       {

               挑出到目前为止对应价值最大者;

       }

 }

       如何判断物品i是否在子集j中呢?还是举上面那个例子:背包容积为maxVolume=4,物品numOfGood=3种,分别(体积,价值,数量)为

                        Good[]={(1,10,1)、(2,20,1)、(4,1,1)}

         此时总的子集数cnt=2^3,当j=3时,其对应二进制位00000011,只有第0、1位为1,则对应取Good[0]、Good[1].若当前子集为j(0<=j<cnt),要判断其中包含的物品组合,只需循环遍历物品i,若(j&1<<(i))非0,则i在子集j中;否则不在。

         位操作枚举法有它的局限,一来1<<rshit很可能溢出,二来时间复杂度为O(2^n)。前者好解决,有单个int型变成int数组,即可解决溢出问题;后者是制约此解法的关键所在。空间复杂度为O(1),时间复杂度为O(2^n)。

 

1.2搜索枚举法

   搜索法也是一种枚举法,但回避了位操作可能带来的溢出问题,而且可以适当的在搜索过程中进行剪枝,从而得到常数级的优化。搜索法是在一棵隐式图的基础上进行的,此图是根据问题的状态空间得到的。

对于物品i,有两个分支:取第i个物品,不取第i个物品。层层递归即可得到一棵解答树,最终求得满足条件的价值最大者。

空间复杂度为O(1),时间复杂度为O(2^n)。

 

2.动态规划法

     特点是:每种物品仅有一件,可以选择放或不放。  

     用子问题定义状态:即dp[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。

     则其状态转移方程便是:  

           dp[i][j]=max{dp[i-1][j],dp[i-1][j-Good[i].volume]+Good[i].price} 

    然后就是对状态转移方程的处理,一般有两种处理方法:

           1.由前往后递推

           2.由后往前递归

     由于递归会伴随函数调用、栈资源消耗等降低效率的因素,所以我们下面给出的搜索算法都回避了递归。

 2.1无优化动态规划

   for i=0..numOfGood-1

      for j=0..maxVolume

        dp[i][j]=max{dp[i-1][j],dp[i-1][j-Good[i].volume]+Good[i].price};

      空间复杂度为O(maxVolume*numOfGood),时间复杂度为O(maxVolume*numOfGood)。时间复杂度叫枚举法有较大改进。

 

2.2有优化动态规划

        通过观察状态转移方程, dp[i][j]=max{dp[i-1][j],dp[i-1][j-Good[i].volume]+Good[i].price} 发现关于当前物品i,求dp[i][j]只与dp[i-1][j]、dp[i-1][j-Good[i].volume]+Good[i].price,前一维i-1相关,于是可以使用滚动数组。只需开辟dp[2][MAXVOLUME],然后滚动求解。

  k=1;

  for i=1..numOfGood-1//第一个物品单独处理

  {

      for j=0..maxVolume

         dp[k][j]=max{dp[k^1][j],dp[k^1][j-Good[i].volume]+Good[i].price};

    k^=1;

  }

 

 

  这样一来空间复杂度有效将至O(maxVolume)。 不过关于此问题,还有一个更为巧妙的方法。在此观察状态转移方程:

            dp[i][j]=max{dp[i-1][j],dp[i-1][j-Good[i].volume]+Good[i].price}

   发现第一维其实与最终要求解的价值最大无多大关系,只是为了防止当前物品使用不止一次。还是上面那个例子:背包容积为maxVolume=4,物品numOfGood=3种,分别(体积,价值,数量)为

                 Good[]={(1,10,1)、(2,20,1)、(4,1,1)}

     若直接开辟dp[MAXVOLUME],顺序处理

 for j=0..maxVolume

        dp[j]=max{dp[j],dp[j-Good[i].volume]+Good[i].price};

 

     则会带来重复使用物品,与01背包本质不符。下面是i=0时所得:

 

体积j

0

1

2

3

4

 

价值

0

10

20

30

40

 

       明显重复了,j从2开始都重复利用了前一次的值,是得物品不知被用了一次。此问题可以通过改变j的求解顺序解决,有顺序变成逆序,

体积j

0

1

2

3

4

价值

0

10

10

10

10

    从而排除了重复使用数目仅为1的物品。具体伪代码为:直接开辟dp[MAXVOLUME],顺序处理

    

 for j=maxVolume到0

        dp[j]=max{dp[j],dp[j-Good[i].volume]+Good[i].price};

 

   空间复杂度为O(maxVolume),时间复杂度为O(maxVolume*numOfGood)。空间复杂度较无优化的动态规划有较大改进。

 

3.遗传算法

      由上述解决方法可知,最好的解法时间复杂度为O(maxVolume*numOfGood),当maxVolume、numOfGood都很大时是无法在有限的时间范围内求出最优解的。

     于是我们想到了演化计算中的遗传算法,根据物品数量numOfGood初始化若干条染色体,每条染色体代表背包的一种装法。还是上面那个例子:

    背包容积为maxVolume=4,物品numOfGood=3种,分别(体积,价值,数量)为

          Good[]={(1,10,1)、(2,20,1)、(4,1,1)}

    我们初始化染色体chroSingle[],若chroSingle[i]为011,则表示取第0、1个物品。然后通过遗传操作:交叉、变异、选择等不断模拟自然界适者生存的法则,最终选择出适应度较高的,对应原问题的一个可行解。当然在遗传操作中应保证所产生的染色体是合法的,即对应物品的体积和不超过背包容积。下面是我们的遗传算法框图:

 

                           

      通常情况下可以得到一个可行的近似解,时间复杂度为(generation*numOfGood^2)。当maxVolume、numOfGood很大尤其是maxVolume很大时,传统的方法都无法在保证效率的情况下得到准确姐,此时可以尝试人工智能的方法。

 

 

4.贪心算法

    当然,当maxVolume、numOfGood达到一定程度时,不管是传统方法还是遗传算法都无法在有限的时间内求解问题,于是很自然地想到贪心算法。在前面提到过,在物品可分的情况下贪心算法是可以得到最优解的。但当物品不可分时,贪心算法在其他方法失效的情况下也是可以求解问题的。提到贪心算法,我们必须想到一个贪心策略,即选取当前最优是依据何种策略评判的。结合01背包问题本身,可以有3种贪心策略,

             a、体积最小

             b、价值最大

             c、价值/体积最大

   当然,这3种贪心策略不能说哪种绝对好,在我们的测试用例中却是也是这样的。

 

 

统计组合数+打印路径

   关于01背包问题,我们基本运用了以上的4大类,8种具体不同算法。同时,我们统计了最优解的组合数并打印了路径。

    (1)、位操作枚举法+遗传算法+贪心算法。统计组合数和打印路径相对简单,在此不多说。

    (2)、搜索法+动态规划法。这两类方法是统计组合数+打印路径的难点所在。但由于思想相似,我们一起说说。

     Int path[MAXN][MAXVOLUME];//存放路径,path[i][j]为0表示在总体积为j时不取第i个物品;path[i][j]为1表示在总体积为j时取第i个物品。当然path[i][j]可以取大于1的数值,表示不只取一个该物品,这是下面完全背包、多重背包的内容,这里先不介绍。

     Int numOfCombine[MAXVOLUME];//存放总体积为j时符合条件的组合数。

下面介绍一下怎么求,怎么用上述两个数组,为了提高效率,我们求上述两个数组是糅合在求dp[][]的功能模块里的。对应伪代码为:

dp[i][j]=dp[i-1][j];

if(dp[j-Good[i].volume]+Good[i].price>dp[i][j])//要i物品价值更大

{

numOfCombine[j]=numOfCombine[j-Good[i].volume];

dp[i][j]=dp[i-1][j-Good[i].volume]+Good[i].price;

path[i][j]=1;

}

else if(dp[i-1][j-Good[i].volume]+Good[i].price==dp[i][j])//要与不要一样大

{

numOfCombine[j]+=numOfCombine[j-Good[i].volume];

path[i][j]=1;//此处path[i][j]可赋值可不赋值,对应路径不一样

}


 

     当然,有些繁琐的细节在此就不说了,注意边界条件、临界状态的把握。

     求得了path[][]数组,下面就是根据里面的类容打印一条路径了。由于路径很多,可能会出现组合爆炸问题,开数组代替path[][]单个值也无法保证记录所有路径,而且很可能程序运行时栈溢出,所以我们只打印了一条路径。我们由后往前推到,i=numOfGood-1,j=maxVolume;                        

 

1、.判断path[i][j]记录容积为j时第i个物品是否取。 

          (1)、为0表示没取,不打印第i个物品,i=i-1;

        (2)、为1表示取过,打印第i个物品,i=i-1,j=j-Good[i].volume。

2、重复步骤1直到i或j其中一个小于0

    上述打印模块也有地归与非递归的,为了效率,我们还是选择了非递归的程序实现。

int j=volume;

for(i从numOfGood-1减到0)

{

if(path[i][j]非0)

{

           打印物品i;

           调整j.

}

}

 

 

 

背包是否装满的处理

      背包是否装满看似无关紧要,其实对问题的决策起到至关重要的作用。重复之前的例子:

      背包容积为maxVolume=4,物品numOfGood=3种,分别(体积,价值,数量)为Good[]={(1,10,1)、(2,20,1)、(4,1,1)}

     在背包装满的情况下为选物品1,总价值为1;

     若不要求未满,则可取物品2+物品3,总体积为1+2=3<4,总价值为10+20=30.

     我们此次选用的是C++,我们把背包抽象为一个类,把与之相关的所有方法都抽象为成员函数,从而实现封装与抽象。为了能同时处理背包装满与不装满问题,我们定义的接口传递了参数isFull,以此处理上述情况。其实满与不满关键在于初始值的设定。

     如果要求恰好装满背包,那么在初始化为

 dp[i][0]=0,         0<=i<=numOfGood-1

       dp[i][j]=-1,       0<=i<=numOfGood-1且1<=j<=maxVolume 

 

      这样就可以保证最终得到的dp[numOfGood-1][maxVolume]是一种恰好装满背包的最优解

 

    如果不要求背包装满,那么在初始化为  

  dp[i][j]=0,       0<=i<=numOfGood-1且0<=j<=maxVolume

 

    为什么呢?可以这样理解:初始化的dp数组事实上就是在没有任何物品放入背包时的合法状态。

     (1)、如果要求背包恰好装满,那么此时只有容量为0的背包可能被价值为0的nothing“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,它们的值就都应该是-1了。

     (2)、如果背包不要求装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

 

 

二、完全背包

      完全背包,即每件物品的数量无限多,

     或者   Good[i].volume*Good[i].number>=maxVolume此时等价于物品数量无限多。

     关于完全背包,枚举法同样可以,可以在01背包的基础上再加一层枚举,但这样时间复杂度达O(2^numOfGood*max(maxVolume/Good[i].volume)),只要numOfGood、maxVolume稍大就很难出结果。所以对于完全背包,我们放弃枚举法。

      遗传算法也是可以的。只是编码上增加了一维,即原来的每一位对应一个位串,位串长度len[i],保证2^len[i]>=maxVolume/Good[i].volume),len[i]取最小(这样既能使物品i足够,又能最大限度节约空间)。由于可能长度很长超内存,不考虑这层又和01背包重复,所以我们没有用遗传算法。

1、动态规划法

     特点是:每种物品足够多,可以选择不放或放多件。  

     用子问题定义状态:即dp[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。

     则其状态转移方程便是:  

dp[i][j]=max{dp[i-1][j],dp[i-1][j-k*Good[i].volume]+k*Good[i].price} 

 

     对比01背包的状态转移方程,可得多了变量k。

 

1.1、无优化动态规划

    只需在原有01背包问题的基础上加一层数量的循环,伪代码如下:

for i=0..numOfGood-1

        for j=0..maxVolume

          for(k=0;k*Good[i].volume<=maxVolume;k++)

           dp[i][j]=max{dp[i-1][j],dp[i-1][j-k*Good[i].volume]+k*Good[i].price};

 

     空间复杂度为O(maxVolume*numOfGood),时间复杂度为O(maxVolume*Σ(maxVolume/Good[i].volume)),这个时间复杂度是很大的。

 

1.2、有优化动态规划

      可以将完全背包换成01背包问题,即把每件物品看做maxVolume/Good[i].volume件体积和价值都不变的物品,于是就成了01背包,但此方法完全没有降低时间复杂度;但我们可以顺着转换为01背包的思想加以改进。

      由二进制原理可知,任何整数k都能表示成二进制形式,于是尝试将完全背包问题分解为物品数为1,2,4,8,…2^e,其中e是小于等于log2(k)的最大整数。然后增加1,2,3,4,8

…2^e对应的体积和价值的物品各一件,剩下就是01背包问题了。这样一来,时间复杂度有效的降为O(maxVolume*Σ(log2(maxVolume/Good[i].volume))),这是一个比较有效的改进。

      但我们还有更有效的改进方法。还记得上面顺着处理01背包带来的物品重复使用吗?在多重背包中,物品的数量是足够多的,不存在物品重复使用带来的错误。

   还是那个例子:

    背包容积为maxVolume=4,物品numOfGood=3种,分别(体积,价值,数量)为

        Good[]={(1,10,1)、(2,20,1)、(4,1,1)}

   直接开辟dp[MAXVOLUME],顺序处理

下面是i=0的情况:

体积

0

1

2

3

4

价值

0

10

20

30

40

  

  所以顺着处理是可以的

for i=0..numOfGood-1

   for j=0..maxVolume

        dp[j]=max{dp[j],dp[j-Good[i].volume]+Good[i].price};

 

这样一来,空间复杂度为O(maxVolume),时间复杂度为O(maxVolume*numOfGood)。无优化的动态规划有较大改进。

 

2、贪心算法

    完全背包的贪心算法基本和01背包一样,在此也就不说了。

    统计组合数+打印路径+背包是否装满

   在这几方面和01背包很类似,只是有些细节需要特殊处理,大家如果遇到过问题后再讨论。在此也不多说了。

 

 

 

三、多重背包

     多重背包,即每件物品的数量有限

     枚举法+遗传算法同样可以解决多重背包问题,但和完全背包一样存在很大的不足,在此也就不说了。

1、动态规划法

     特点是:每种物品足够多,可以选择不放或放多件。  

     用子问题定义状态:即dp[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。

     则其状态转移方程便是:  

dp[i][j]=max{dp[i-1][j],dp[i-1][j-k*Good[i].volume]+k*Good[i].price} 

 

 对比01背包的状态转移方程,可得多了变量k;对比完全背包,可知对k又多了限制条件:K<=Good[i].number

 1.1、无优化+不合并动态规划

      在此说一下,这里的优化是指像完全背包那样顺着处理降低时空复杂度,提高效率。

      此处基本同完全背包,只是注意   

     多重背包:k<=min(j/Good[i].GetVolume(),Good[i].GetNumber())

     完全背包:k=j/Good[i].GetVolume()

     空间复杂度为O(maxVolume*numOfGood),时间复杂度为O(maxVolume*Σ(Good[i].volume)),这个时间复杂度是很大的。

 

1.2、有优化+不合并动态规划

       此处基本同完全背包,只是注意当Good[i].GetVolume()*Good[i].GetNumber()>=volume,对该物品来说就是完全背包; 否则,按照完全背包的思路顺着处理。这样一来,空间复杂度为O(maxVolume),时间复杂度为O(maxVolume*numOfGood)。无优化的动态规划有较大改进。

1.3、有优化+合并动态规划

     这里是指顺序处理+按二进制合并。关于二进制合并在完全背包中已经有所介绍,参完全背包部分,在此不多说。

 

2、贪心算法

       与01背包唯一不同就是物品可多选,然后就是小细节。

 

统计组合数+打印路径+背包是否装满

   多重背包问题可以转换为01背包+完全背包,这方面基本同上述二者。

 

下面贴代码:

 

染色体类头文件Chromosome.h

#ifndef CHROMOSOME_H
#define CHROMOSOME_H

#include<iostream>
#include<bitset>
#include<ctime>
#include<vector>
#include<cstring>
#include"Good.h"
using namespace std;

const int MAXN_LEN=100;
const int MAXN_NUM=10*MAXN_LEN;


class Chromosome
{
public:
	void GetMutate(Chromosome &,int);
	int GetPostion(int);
	int GetVolume();
	void SetPostion(int,int);
	int GetFitness(vector<Good>&);
	bool IsIllegal(vector<Good>&)const;
	void InitiateChro(vector<Good>&,int,int);
	void GetCross(Chromosome &,int,int);
	void GetMutate(int);
	void PrintChro(vector<Good>&);
	Chromosome operator=(Chromosome);
	bitset<MAXN_LEN> GetChro();
private:
	int volume;
	int length;
	bitset<MAXN_LEN>myChro;
};

/***************************************************
*参数:无
*功能:返回染色体对应极限容量
****************************************************/
int Chromosome::GetVolume()
{
	return volume;
}

/***************************************************
*参数:无
*功能:返回染色体
****************************************************/
bitset<MAXN_LEN> Chromosome::GetChro()
{
	return myChro;
}

/***************************************************
*参数:tChromosome染色体基因
*功能:重载赋值操作符
****************************************************/
Chromosome Chromosome::operator=(Chromosome tChromosome)
{
	length=tChromosome.length;
	volume=tChromosome.volume;
	for(int i=0;i<length;i++)
		myChro[i]=tChromosome.GetPostion(i);
	return *this;
}

/***************************************************
*参数:id染色体基因
*功能:返回染色体基因
****************************************************/
int Chromosome::GetPostion(int id)
{
	return myChro[id];
}

/***************************************************
*参数:id染色体基因,newBit表示设置状态
*功能:设置染色体基因
****************************************************/
void Chromosome::SetPostion(int id,int newBit)
{
	myChro[id]=newBit;
}

/***************************************************
*参数:物品容器
*功能:打印染色体对应的物品组合
****************************************************/
void Chromosome::PrintChro(vector<Good>& myGood)
{
	for(int i=0;i<length;i++)
	{
		if(myChro[i])
			cout<<"("<<myGood[i].GetVolume()<<","<<myGood[i].GetPrice()<<")";
	}
	cout<<endl;
}

/***************************************************
*参数:chroId染色体Id
*功能:判断染色体是否合法
****************************************************/
bool Chromosome::IsIllegal(vector<Good>& myGood)const
{
	bool isIllegal=true;
	int tempWei=0;
	for(int i=0;i<length;i++)
	{
		if(myChro[i])
	    	tempWei+=myGood[i].GetVolume();
	}
	if(tempWei>volume)
		isIllegal=false;
	return isIllegal;
}

/***************************************************
*参数:无
*功能:初始化合法染色体
****************************************************/
void Chromosome::InitiateChro(vector<Good>& myGood,int tlength,int tVolume)
{
	length=tlength;
	volume=tVolume;
	srand(unsigned(clock()));
	int num=0;
	do
	{
		for(int i=0;i<length;i++)
			SetPostion(i,rand()%2);
		num++;
		if(num>=100)break;
	}while(!IsIllegal(myGood));
	if(num>=100)//无法在规定次数内完成染色体的初始化
	{
     	for(int i=0;i<length;i++)
			SetPostion(i,0);
	}
}


/***************************************************
*参数:chroId染色体Id
*功能:计算染色体的适应度
****************************************************/
int Chromosome::GetFitness(vector<Good>& myGood)
{
	int fitness=0;
	for(int i=0;i<length;i++)
	{
		if(myChro[i])
	    	fitness+=myGood[i].GetPrice();;
	}
	return fitness;
}

/***************************************************
*参数:chroId染色体Id,pos为变异的位置
*功能:染色体基因变异
****************************************************/
void Chromosome::GetMutate(int pos)
{
	myChro.flip(pos);
}

/***************************************************
*参数:chr1,chr2染色体Id,[st,en]染色体交叉区间
*功能:染色体交叉
****************************************************/
void Chromosome::GetCross(Chromosome &tChromosome,int st,int en)
{
	int temp;
	for(int i=st;i<=en;i++)
	{
		temp=myChro[i];
		myChro[i]=tChromosome.myChro[i];
		tChromosome.myChro[i]=temp;
	}
}

#endif


种群类头文件Population.h

#ifndef POPULATION_H
#define POPULATION_H

#include<iostream>
#include<bitset>
#include<ctime>
#include<cstring>
#include<cmath>
#include"Good.h"
#include"Chromosome.h"
#include"Population.h"
using namespace std;

class Population
{
public:
	Population(int,int,int,int,double,double,vector<Good>&);
	void GeneticAlgorithm(vector<Good>&);
	int Selection(vector<Good>&);
private:
	int maxTotalPrice;
	int volume;
    int generation;
	int length;
	int number;
	double mutationRate;
	double crossionRate;
	Chromosome chroSingle[MAXN_NUM];
	Chromosome bestChro;
};


/***************************************************
*参数:tGeneration,tnumber,tlength,tmutationRate,tcrossionRate
分别表示种群中进化代数,染色体数目,长度,变异率,交叉率
*功能:构造种群
****************************************************/
Population::Population(int tVolume,int tGeneration,int tnumber,int tlength,double tmutationRate,double tcrossionRate,vector<Good>& myGood)
{	
	volume=tVolume;
	generation=tGeneration;
	number=tnumber;
	length=tlength;
	mutationRate=tmutationRate;
	crossionRate=tcrossionRate;
	maxTotalPrice=-1;
	for(int i=0;i<tnumber;i++)
	{
		chroSingle[i].InitiateChro(myGood,length,volume);
	}
}

/***************************************************
*参数:无
*功能:选择适应度高的染色体
****************************************************/
int Population::Selection(vector<Good>& myGood)
{
	int i,bestId=-1;
	int ret=-1;
	double sumFitness=0;
	int fitness[MAXN_NUM];
	double countRate[MAXN_NUM];
	double selRate[MAXN_NUM];
	for(i=0;i<number;i++)
	{
		fitness[i]=chroSingle[i].GetFitness(myGood);
		if(ret<fitness[i])
		{
			ret=fitness[i];
			bestId=i;
		}
		sumFitness+=1.0*fitness[i];
	}
	if(maxTotalPrice<ret)
	{
		maxTotalPrice=ret;
		bestChro=chroSingle[bestId];
	}
	for(i=0;i<number;i++)
	{
		countRate[i]=(1.0*fitness[i])/sumFitness;
	}
	
	selRate[0]=countRate[0];
	for(i=1;i<number;i++)
	{
		selRate[i]=selRate[i-1]+countRate[i];
	}
	int num=0;	

	Chromosome TempChroSingle[MAXN_NUM];
	
	srand(unsigned(clock()));
	while(num<number)
	{
	//	cout<<"num======"<<num<<endl;
		double rate=(1.0*rand())/RAND_MAX;
		for(i=0;i<number;i++)
		{
			if(selRate[i]>rate)
				break;
		}
		if(i==number)
			--i;
		TempChroSingle[num]=chroSingle[i];
		++num;
	}
	for(i=0;i<number;i++)
	{
		chroSingle[i]=TempChroSingle[i];
	}
	return ret;
}

/***************************************************
*参数:无
*功能:遗传算法主函数
****************************************************/
void Population::GeneticAlgorithm(vector<Good>& myGood)
{
	int nowGeneration=0;
	int maxTotalPrice=0,tempTotalPrice=-1;
	double mumCross=0,mumMutat=0;
	while(nowGeneration<generation)
	{
		mumMutat+=mutationRate*number;
		mumCross+=crossionRate*number;

	//	cout<<"&&&&&&&&&&"<<endl;
		while(mumCross>1.0)
		{
			Chromosome temp1,temp2;
			int id1,id2;
			srand(unsigned(clock()));
			do
			{
			id1=rand()%number;
			id2=rand()%number;
			int st,en;
				st=rand()%length;
				en=rand()%length;
			if(st>en)
			{
				st=st^en;
				en=st^en;
				st=st^en;
			}
			temp1=chroSingle[id1];
			temp2=chroSingle[id2];
			temp1.GetCross(temp2,st,en);
			}while(!temp1.IsIllegal(myGood)||!temp2.IsIllegal(myGood));
			chroSingle[id1]=temp1;
			chroSingle[id2]=temp2;
			mumCross=mumCross-1;
		}
		while(mumMutat>1.0)
		{
			int bel,id;
			Chromosome temp;
			do
			{
				srand(unsigned(clock()));
			    bel=rand()%number;
				temp=chroSingle[bel];
				id=rand()%length;
				temp.GetMutate(id);
			}while(!temp.IsIllegal(myGood));
			chroSingle[id]=temp;
			mumMutat=mumMutat-1;
		}
	//	cout<<"+++++++++++++++++++"<<endl;
		tempTotalPrice=Selection(myGood);
		if(maxTotalPrice<tempTotalPrice)
			maxTotalPrice=tempTotalPrice;
	//	cout<<"nowGeneration="<<nowGeneration<<endl;
		++nowGeneration;
	}
	cout<<"其中一个组合为:"<<endl;
	bestChro.PrintChro(myGood);
	cout<<"遗传算法得到的近似精确值为:"<<maxTotalPrice<<endl;
}

#endif


 

 

物品类头文件Good.h

#ifndef GOOD_H
#define GOOD_H
#include<iostream>
using namespace std;

class Good
{
public:
	Good(int vol,int pri,int num)
	{
		price=pri;
		volume=vol;
		number=num;
	}
	Good(const Good& good)
	{
	price=good.price;
	volume=good.volume;
	number=good.number;
	}
	int GetPrice();
	int GetVolume();
	int GetNumber();
private:
	int price;
	int volume;
	int number;
};

/***************************************************
*依次返回待处理物品价值、体积、数量
****************************************************/
int Good::GetPrice()
{
	return price;
}
int Good::GetVolume()
{
	return volume;
}
int Good::GetNumber()
{
	return number;
}

#endif


 

背包类头文件Pack.h

#ifndef PACK_H
#define PACK_H

#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#include"Good.h"
#include"Chromosome.h"
#include"Population.h"
using namespace std;

const int MAXN=100;
const int INF=-(1<<30l);
const int MAXVOLUME=100;

int max(int a,int b)
{
	return a<b?b:a;
}

int min(int a,int b)
{
	return a<b?a:b;
}

class Pack
{
public :
	void SetPackPopulation(int tVolume,int tGeneration,int tNumber,double tMutation,double tCrossion);
	void SetPack(int tVolume);
	int GetNumberOfGoods();
	int GetVolume();
	Good Pack::GetPositionGood(int);
	vector<Good>&  GetVector();
	void Pack::PushBack(vector<Good>&,Good &);
	int SelectGoodOfGreed(bool);
	int GreedOfGetMinVolume(bool);
	int GreedOfGetMaxPrice(bool);
	int GreedOfGetMaxDensity(bool);
	void InitateDPArrayOfNotOptimize(bool);
	void InitateDPArray(bool);
	int BitOperationEnum(bool);
	int DFS(int,int ,bool);
	int searchEnum(bool);	
	int SolveZeroOnePackOfNotOptimize(bool);
	void SingleZeroOnePack(Good&,int,int,bool);
    int SolveZeroOnePack(bool);
	int SolveCompletePackOfNotOptimize(bool);
	void SingleCompletePack(Good&,int,int,bool);
    int SolveCompletePack(bool);
	int SolveMultiplePackOfNotMergeOfNotOptimize(bool);
	void SingleMultiplePackOfNotMerge(Good&,int,bool);
    int SolveMultiplePackOfNotMerge(bool);
	void SingleMultiplePackOfMerge(Good&,int,bool);
    int SolveMultiplePackOfMerge(bool);
	void PrintZeroOnePack();
	void PrintCompletePack();
	void PrintMultiplePack();
	void PrintWhatSelected();
private:	
	int DFSNumOfCombine[MAXN][MAXVOLUME];
	int path[MAXN][MAXVOLUME];
	int dpOfNotOptimize[MAXN][MAXVOLUME];
	int dp[MAXVOLUME];
	int numOfCombine[MAXVOLUME];
	int volume;
	int maxTotalPrice;
	vector<Good>myGood;
	vector<Good>myLoadGood;
};

/***************************************************
*参数:tVolume背包容量
*功能:初始化背包
****************************************************/
void Pack::SetPack(int tVolume)
{
	volume=tVolume;
}

/***************************************************
*参数:
*功能:设置种群信息
****************************************************/
void Pack::SetPackPopulation(int tVolume,int tGeneration,int tNumber,double tMutation,double tCrossion)
{
	Population pop(tVolume,tGeneration,tNumber*5,tNumber,tMutation,tCrossion,myGood);
    pop.GeneticAlgorithm(myGood);
}

/***************************************************
*参数:无
*功能:返回物品容器
****************************************************/
vector<Good>&  Pack::GetVector()
{
	return myGood;
}

/***************************************************
*返回背包的待处理物品数量
****************************************************/
int Pack::GetNumberOfGoods()
{
	return myGood.size();
}

/***************************************************
*返回背包的最大容积
****************************************************/
int Pack::GetVolume()
{
	return volume;
}

/***************************************************
*获得对应下标的物品
****************************************************/
Good Pack::GetPositionGood(int id)
{
	if(id>=myGood.size())
	{
		cout<<"下标越界!"<<endl;
	}
	else
	{
		return myGood[id];
	}
}

/***************************************************
*末未添加物品
****************************************************/
void Pack::PushBack(vector<Good>&myVec,Good &good)
{
	myVec.push_back(good);
}

/***************************************************
*参数:无
*功能:打印01背包一条路径
****************************************************/
void Pack::PrintZeroOnePack()
{
	int vol=volume;
	for(int id=GetNumberOfGoods()-1;id>=0;id--)
	{
		if(path[id][vol])
		{
		    Good temp=GetPositionGood(id);
			cout<<"("<<temp.GetVolume()<<","<<temp.GetPrice()<<","<<path[id][vol]<<")";
			vol-=temp.GetVolume();
		}
	}
	cout<<endl;
}

/***************************************************
*参数:无
*功能:打印完全背包一条路径
****************************************************/
void Pack::PrintCompletePack()
{
	int vol=volume;
	int id=GetNumberOfGoods()-1;
	int tmpNum=0;
	int isFirst=true;
	while(vol>=0&&id>=0)
	{
		Good temp=GetPositionGood(id);
		if(path[id][vol])
		{
			isFirst=false;
			vol-=temp.GetVolume();
			++tmpNum;
		}
		if(!path[id][vol])
		{
			if(!isFirst)
			{
				cout<<"("<<temp.GetVolume()<<","<<temp.GetPrice()<<","<<tmpNum<<")";
				tmpNum=0;
				isFirst=true;
			}
			id--;
		}
	}
	cout<<endl;
}

/***************************************************
*参数:无
*功能:打印多重背包一条路径
****************************************************/
void Pack::PrintMultiplePack()
{
	int vol=volume;
	int id=GetNumberOfGoods()-1;
	while(vol>=0&&id>=0)
	{
		if(path[id][vol])
		{
		    Good temp=GetPositionGood(id);
			cout<<"("<<temp.GetVolume()<<","<<temp.GetPrice()<<","<<path[id][vol]<<")";
			vol-=path[id][vol]*temp.GetVolume();
		}
		  id--;
	}
	cout<<endl;
}


/***************************************************
*参数:
*功能:打印物品组合
****************************************************/
void Pack::PrintWhatSelected()
{
	cout<<"其中一个组合为:";
	for(vector<Good>::iterator iter=myLoadGood.begin();iter!=myLoadGood.end();++iter)
	{
		cout<<"("<<iter->GetVolume()<<","<<iter->GetPrice()<<","<<iter->GetNumber()<<")";
	}
	cout<<endl;
}

/***************************************************
*贪心法根据排序好的容器选择
****************************************************/
int Pack::SelectGoodOfGreed(bool isInfinity)
{
	int tempVol=volume,i=0,retPri=0,perNum;
	myLoadGood.clear();
	if(isInfinity)
	{
		Good tmp=GetPositionGood(0);
		PushBack(myLoadGood,Good(tmp.GetVolume(),tmp.GetPrice(),tempVol/tmp.GetVolume()));
		retPri=tempVol/tmp.GetVolume()*tmp.GetPrice();
	}
	else 
	{
	while(i<GetNumberOfGoods())
	{
		Good tmp=GetPositionGood(i);
		perNum=tempVol/tmp.GetVolume();
		perNum=perNum<tmp.GetNumber()?perNum:tmp.GetNumber();
		if(0==perNum)break;
		tempVol-=perNum*tmp.GetVolume();
		if(tempVol>=0)
		{
			retPri+=perNum*tmp.GetPrice();
			PushBack(myLoadGood,Good(tmp.GetVolume(),tmp.GetPrice(),perNum));
		}
		else break;
		i++;
	}
	}
	PrintWhatSelected();
	return retPri;
}

/***************************************************
*贪心法每次选体积最小物品背包最大价值
****************************************************/
bool CmpMinVolume(Good a,Good b)
{
	return a.GetVolume()<b.GetVolume();
}
int Pack::GreedOfGetMinVolume(bool isInfinity)
{
	sort(myGood.begin(),myGood.end(),CmpMinVolume);
	maxTotalPrice=SelectGoodOfGreed(isInfinity);
	return maxTotalPrice;
}

/***************************************************
*贪心法每次选价值最大物品背包最大价值
****************************************************/
bool CmpMaxPrice(Good a,Good b)
{
	return a.GetPrice()>b.GetPrice();
}
int Pack::GreedOfGetMaxPrice(bool isInfinity)
{
	sort(myGood.begin(),myGood.end(),CmpMaxPrice);
	maxTotalPrice=SelectGoodOfGreed(isInfinity);
	return maxTotalPrice;
}

/***************************************************
*贪心法每次选价值/体积最大物品背包最大价值
****************************************************/
bool CmpMaxDensity(Good a,Good b)
{
	return (double)(a.GetPrice())/a.GetVolume()>(double)(b.GetPrice())/b.GetVolume();
}
int Pack::GreedOfGetMaxDensity(bool isInfinity)
{
	sort(myGood.begin(),myGood.end(),CmpMaxDensity);
	maxTotalPrice=SelectGoodOfGreed(isInfinity);
	return maxTotalPrice;
}


/***************************************************
*对应01背包的位操作枚举求法
****************************************************/
int Pack::BitOperationEnum(bool isFull)
{
	int numOfGoods=GetNumberOfGoods();
	if(numOfGoods>15)
	{
		cout<<"处理物品过多,不适合位操作!"<<endl;
		return -1;
	}
	maxTotalPrice=-1;
	int cnt=1<<numOfGoods;
	int i,j,set=0,sumPrice,sumVolume,sumNum=0;
	for(i=0;i<cnt;i++)
	{
		sumPrice=0;
		sumVolume=0;
		for(j=0;j<numOfGoods;j++)
		{
			if((1<<j)&i)
			{
				Good temp=GetPositionGood(j);
				sumVolume+=temp.GetVolume();
				if(sumVolume>volume)
					break;
				sumPrice+=temp.GetPrice();
			}
		}
		if(j>=numOfGoods&&sumVolume<=volume&&sumPrice>=maxTotalPrice)
		{
			if(!isFull||(isFull&&sumVolume==volume))
			{
				if(sumPrice==maxTotalPrice)
				{
					sumNum++;
				}
				else
				{
					sumNum=1;
					set=i;
					maxTotalPrice=sumPrice;
				}
			}
		}
	}
	myLoadGood.clear();
	for(i=0;i<numOfGoods;i++)
	{
		if((1<<i)&set)
		{
			PushBack(myLoadGood,GetPositionGood(i));
		}
	}
	cout<<"总的组合数为:"<<sumNum<<endl;
	cout<<"其中一个组合为:"<<endl;
	PrintWhatSelected();
	return maxTotalPrice;
}


/***************************************************
*对应01背包的搜索枚举求法DFS
****************************************************/
int Pack::DFS(int start,int tvol,bool isFull)
{
	if(start<0)
	{
		if(!isFull||(isFull&&0==tvol))
		{
			return 0;
		}
		else return -1;
	}
	int tmp1=DFS(start-1,tvol,isFull);//不要此背包
	int tmp2=-1;
	if(tvol>=GetPositionGood(start).GetVolume())
	{
	    tmp2=DFS(start-1,tvol-GetPositionGood(start).GetVolume(),isFull);//要此背包
		if(-1!=tmp2)
			tmp2+=GetPositionGood(start).GetPrice();
	}
	if(tmp1<tmp2)
	{
		path[start][tvol]=1;
		if(0==start)
			DFSNumOfCombine[start][tvol]=1;
		else DFSNumOfCombine[start][tvol]=DFSNumOfCombine[start-1][tvol-GetPositionGood(start).GetVolume()];
		return tmp2;
	}
	else if(tmp1>tmp2)
	{
		path[start][tvol]=0;
		if(0==start)
			DFSNumOfCombine[start][tvol]=1;
		else DFSNumOfCombine[start][tvol]=DFSNumOfCombine[start-1][tvol];
		return tmp1;
	}
	else 
	{
		path[start][tvol]=0;
		if(-1!=tmp1)
		DFSNumOfCombine[start][tvol]=DFSNumOfCombine[start-1][tvol]+
			DFSNumOfCombine[start-1][tvol-GetPositionGood(start).GetVolume()];
		else DFSNumOfCombine[start][tvol]==0;
		return tmp1;
	}
}
/***************************************************
*对应01背包的搜索枚举求法主函数
****************************************************/
int Pack::searchEnum(bool isFull)
{
	if(GetNumberOfGoods()>=20)
	{
		cout<<"物品太多,不合适枚举!"<<endl;
		return -1;
	}
	maxTotalPrice=0;
	InitateDPArray(isFull);

	memset(DFSNumOfCombine,0,sizeof(DFSNumOfCombine));
	for(int i=0;i<=volume;i++)
		DFSNumOfCombine[0][i]=1;

	maxTotalPrice=DFS(GetNumberOfGoods()-1,GetVolume(),isFull);
	cout<<"总的组合数为:"<<DFSNumOfCombine[GetNumberOfGoods()-1][volume]<<endl;
	cout<<"其中一个组合为:"<<endl;
	PrintZeroOnePack();
	return maxTotalPrice;
}

/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:初始化DP数组(优化)(无优化)
****************************************************/
void Pack::InitateDPArray(bool isFull)
{
	if(isFull)
	{
		memset(dp,-1,sizeof(dp));
		dp[0]=0;
	}
	else
		memset(dp,0,sizeof(dp));
	memset(path,0,sizeof(path));
	memset(numOfCombine,0,sizeof(numOfCombine));
}

/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:初始化DP数组(无优化)
****************************************************/
void Pack::InitateDPArrayOfNotOptimize(bool isFull)
{
	if(isFull)
	{
		memset(dpOfNotOptimize,-1,sizeof(dpOfNotOptimize));
		dpOfNotOptimize[0][0]=0;
	}
	else
		memset(dpOfNotOptimize,0,sizeof(dpOfNotOptimize));
	memset(path,0,sizeof(path));
	memset(numOfCombine,0,sizeof(numOfCombine));
}

/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:01背包的动态规划求法(无优化)
****************************************************/
int Pack::SolveZeroOnePackOfNotOptimize(bool isFull)
{
	InitateDPArrayOfNotOptimize(isFull);
	for(int i=0;i<GetNumberOfGoods();i++)
	{
		Good temp=GetPositionGood(i);
	//	for(int j=0;j<=volume;j++)//此处不反应该有错???
		for(int j=volume;j>=0;j--)
		{
			if(0==i)
			{
				if(!isFull)
				{
					if(j>=temp.GetVolume())
					{
						dpOfNotOptimize[i][j]=temp.GetPrice();
						path[i][j]=1;
					}
					numOfCombine[j]=1;
				}
				else
				{
					if(j==temp.GetVolume())
					{
						dpOfNotOptimize[i][j]=temp.GetPrice();
						path[i][j]=1;
						numOfCombine[j]=1;
					}
				}
			}
			else
			{
				dpOfNotOptimize[i][j]=dpOfNotOptimize[i-1][j];
				if(j>=temp.GetVolume()&&-1!=dpOfNotOptimize[i-1][j-temp.GetVolume()])
				{
					if(dpOfNotOptimize[i-1][j-temp.GetVolume()]+temp.GetPrice()>dpOfNotOptimize[i][j])
					{
						if(0==numOfCombine[j-temp.GetVolume()])
							numOfCombine[j]=1;
						else numOfCombine[j]=numOfCombine[j-temp.GetVolume()];
						dpOfNotOptimize[i][j]=dpOfNotOptimize[i-1][j-temp.GetVolume()]+temp.GetPrice();
						path[i][j]=1;
					}
					else if(dpOfNotOptimize[i-1][j-temp.GetVolume()]+temp.GetPrice()==dpOfNotOptimize[i][j])
					{
						if(0==numOfCombine[j-temp.GetVolume()])
							numOfCombine[j]+=1;
						numOfCombine[j]+=numOfCombine[j-temp.GetVolume()];
					}
				}
			}
		}
	}
	cout<<"总的组合数为:"<<numOfCombine[volume]<<endl;
	cout<<"其中一个组合为:"<<endl;
	//PrintAllPack();
	PrintZeroOnePack();
	return dpOfNotOptimize[GetNumberOfGoods()-1][volume];
}


/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:对应单个物品01背包的动态规划求法(优化)
****************************************************/
void Pack::SingleZeroOnePack(Good& temp,int id,int tNum,bool isFull)
{
    for(int i=volume;i>=temp.GetVolume();i--)
		if(!isFull||(isFull&&-1!=dp[i-temp.GetVolume()]))
		{
			//dp[i]=max(dp[i],dp[i-temp.GetVolume()]+temp.GetPrice());		
			if(-1!=dp[i-temp.GetVolume()]&&dp[i-temp.GetVolume()]+temp.GetPrice()>dp[i])
			{
				if(0==numOfCombine[i-temp.GetVolume()])
					numOfCombine[i]=1;
				else numOfCombine[i]=numOfCombine[i-temp.GetVolume()];
				dp[i]=dp[i-temp.GetVolume()]+temp.GetPrice();
				path[id][i]=tNum;
			}
			else if(-1!=dp[i-temp.GetVolume()]&&dp[i-temp.GetVolume()]+temp.GetPrice()==dp[i])
			{
				if(0==numOfCombine[i-temp.GetVolume()])
					numOfCombine[i]+=1;
				numOfCombine[i]+=numOfCombine[i-temp.GetVolume()];
			}
		}
}
/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:对应所有物品01背包的动态规划求法
****************************************************/
int Pack::SolveZeroOnePack(bool isFull)
{
	InitateDPArray(isFull);
	maxTotalPrice=0;
	for(int i=0;i<GetNumberOfGoods();i++)
		SingleZeroOnePack(GetPositionGood(i),i,1,isFull);
	maxTotalPrice=dp[volume];
	cout<<"总的组合数为:"<<numOfCombine[volume]<<endl;
	cout<<"其中一个组合为:"<<endl;
	PrintZeroOnePack();
//	PrintAllPack();
	return maxTotalPrice;
}


/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:完全背包的动态规划求法(无优化)
****************************************************/
int Pack::SolveCompletePackOfNotOptimize(bool isFull)
{
	InitateDPArrayOfNotOptimize(isFull);
	for(int i=0;i<GetNumberOfGoods();i++)
	{
		Good temp=GetPositionGood(i);
		for(int j=volume;j>=0;j--)
	//	for(int j=0;j<=volume;j++)
		{
			if(0==i)
			{
				int t=j/temp.GetVolume();///
				for(int k=t;k>0;k--)
				//for(int k=1;j>=k*temp.GetVolume();k++)
				{
					if(!isFull)
					{
						if(j>=k*temp.GetVolume()&&dpOfNotOptimize[i][j]<k*temp.GetPrice())
						{
							dpOfNotOptimize[i][j]=k*temp.GetPrice();
							path[i][j]=k;
						}
						numOfCombine[j]=1;
					}
					else
					{
						if(j==k*temp.GetVolume()&&dpOfNotOptimize[i][j]<k*temp.GetPrice())
						{
							dpOfNotOptimize[i][j]=k*temp.GetPrice();
							path[i][j]=k;
							numOfCombine[j]=1;
						}
					}
				}
			}
			else
			{
				dpOfNotOptimize[i][j]=dpOfNotOptimize[i-1][j];
				int t=j/temp.GetVolume();///
				for(int k=t;k>0;k--)
				//for(int k=1;j>=k*temp.GetVolume();k++)
				{
					if(j>=k*temp.GetVolume()&&-1!=dpOfNotOptimize[i-1][j-k*temp.GetVolume()])
					{
						if(dpOfNotOptimize[i-1][j-k*temp.GetVolume()]+k*temp.GetPrice()>dpOfNotOptimize[i][j])
						{
							if(0==numOfCombine[j-k*temp.GetVolume()])
								numOfCombine[j]=1;
							else numOfCombine[j]=numOfCombine[j-k*temp.GetVolume()];
							dpOfNotOptimize[i][j]=dpOfNotOptimize[i-1][j-k*temp.GetVolume()]+k*temp.GetPrice();
							path[i][j]=k;
						}
						else if(dpOfNotOptimize[i-1][j-k*temp.GetVolume()]+k*temp.GetPrice()==dpOfNotOptimize[i][j])
						{
							if(0==numOfCombine[j-k*temp.GetVolume()])
								numOfCombine[j]+=1;
							numOfCombine[j]+=numOfCombine[j-k*temp.GetVolume()];
						}
					}
				}
			}
		}
	}
	cout<<"总的组合数为:"<<numOfCombine[volume]<<endl;
	cout<<"其中一个组合为:"<<endl;
	//PrintAllPack();
	PrintCompletePack();
	return dpOfNotOptimize[GetNumberOfGoods()-1][volume];
}


/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:对应单个物品完全背包的动态规划求法
****************************************************/
void Pack::SingleCompletePack(Good& temp,int id,int tNum,bool isFull)
{
    for(int i=temp.GetVolume();i<=volume;i++)
		if(!isFull||(isFull&&-1!=dp[i-temp.GetVolume()]))
		{
			//	dp[i]=max(dp[i],dp[i-temp.GetVolume()]+temp.GetPrice());
			if(-1!=dp[i-temp.GetVolume()]&&dp[i-temp.GetVolume()]+temp.GetPrice()>dp[i])
			{
				if(0==numOfCombine[i-temp.GetVolume()])
					numOfCombine[i]=1;
				else numOfCombine[i]=numOfCombine[i-temp.GetVolume()];
				dp[i]=dp[i-temp.GetVolume()]+temp.GetPrice();
				path[id][i]=tNum;
			}
			else if(-1!=dp[i-temp.GetVolume()]&&dp[i-temp.GetVolume()]+temp.GetPrice()==dp[i])
			{
				if(0==numOfCombine[i-temp.GetVolume()])
					numOfCombine[i]+=1;
				numOfCombine[i]+=numOfCombine[i-temp.GetVolume()];
			}	
		}
}
/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:对应所有物品完全背包的动态规划求法
****************************************************/
int Pack::SolveCompletePack(bool isFull)
{
	InitateDPArray(isFull);
	maxTotalPrice=0;
	for(int i=0;i<GetNumberOfGoods();i++)
		SingleCompletePack(GetPositionGood(i),i,1,isFull);
	maxTotalPrice=dp[volume];
	cout<<"总的组合数为:"<<numOfCombine[volume]<<endl;
	cout<<"其中一个组合为:"<<endl;
	//PrintAllPack();
	PrintCompletePack();
	return maxTotalPrice;
}


/***************************************************
*参数:isFull为真表示需装满,为假则无需装满
*功能:多重背包的动态规划求法(不合并+无优化)
****************************************************/
int Pack::SolveMultiplePackOfNotMergeOfNotOptimize(bool isFull)
{
	InitateDPArrayOfNotOptimize(isFull);
	for(int i=0;i<GetNumberOfGoods();i++)
	{
		Good temp=GetPositionGood(i);
		for(int j=volume;j>=0;j--)
	//	for(int j=0;j<=volume;j++)
		{
			if(0==i)
			{
				int t=j/temp.GetVolume();///
				t=min(t,temp.GetNumber());
				for(int k=t;k>0;k--)
				//for(int k=1;j>=k*temp.GetVolume();k++)
				{
					if(!isFull)
					{
						if(j>=k*temp.GetVolume()&&dpOfNotOptimize[i][j]<k*temp.GetPrice())
						{
							dpOfNotOptimize[i][j]=k*temp.GetPrice();
							path[i][j]=k;
						}
						numOfCombine[j]=1;
					}
					else
					{
						if(j==k*temp.GetVolume()&&dpOfNotOptimize[i][j]<k*temp.GetPrice())
						{
							dpOfNotOptimize[i][j]=k*temp.GetPrice();
							numOfCombine[j]=1;
							path[i][j]=k;
						}
					}
				}
			}
			else
			{
				dpOfNotOptimize[i][j]=dpOfNotOptimize[i-1][j];
				int t=j/temp.GetVolume();///
				t=min(t,temp.GetNumber());
				for(int k=t;k>0;k--)
				//for(int k=1;j>=k*temp.GetVolume();k++)
				{
					if(j>=k*temp.GetVolume()&&-1!=dpOfNotOptimize[i-1][j-k*temp.GetVolume()])
					{
						if(dpOfNotOptimize[i-1][j-k*temp.GetVolume()]+k*temp.GetPrice()>dpOfNotOptimize[i][j])
						{
							if(0==numOfCombine[j-k*temp.GetVolume()])
								numOfCombine[j]=1;
							else numOfCombine[j]=numOfCombine[j-k*temp.GetVolume()];
							dpOfNotOptimize[i][j]=dpOfNotOptimize[i-1][j-k*temp.GetVolume()]+k*temp.GetPrice();
							path[i][j]=k;
						}
						else if(dpOfNotOptimize[i-1][j-k*temp.GetVolume()]+k*temp.GetPrice()==dpOfNotOptimize[i][j])
						{
							if(0==numOfCombine[j-k*temp.GetVolume()])
								numOfCombine[j]+=1;
							numOfCombine[j]+=numOfCombine[j-k*temp.GetVolume()];
						}
					}
				}
			}
		}
	}
	cout<<"总的组合数为:"<<numOfCombine[volume]<<endl;
	cout<<"其中一个组合为:"<<endl;
	PrintMultiplePack();
	return dpOfNotOptimize[GetNumberOfGoods()-1][volume];
}

/***************************************************
*对应单个物品多重背包的动态规划求法(不合并)
****************************************************/
void Pack::SingleMultiplePackOfNotMerge(Good& temp,int id,bool isFull)
{
    if(temp.GetVolume()*temp.GetNumber()>=volume)
		SingleCompletePack(temp,id,1,isFull);
    else
    {
		for(int i=0;i<temp.GetNumber();i++)
		   SingleZeroOnePack(temp,id,1,isFull);
    }
}
/***************************************************
*对应所有物品多重背包的动态规划求法(不合并)
****************************************************/
int Pack::SolveMultiplePackOfNotMerge(bool isFull)
{
	InitateDPArray(isFull);
	maxTotalPrice=0;
	for(int i=0;i<GetNumberOfGoods();i++)
	{
		SingleMultiplePackOfNotMerge(GetPositionGood(i),i,isFull);
	}
	maxTotalPrice=dp[volume];
/*	cout<<"总的组合数为:"<<numOfCombine[volume]<<endl;
	cout<<"其中一个组合为:"<<endl;
	PrintAllPack();
	PrintMultiplePack();
	*/
	SolveMultiplePackOfNotMergeOfNotOptimize(isFull);//此处打印有问题,出此下策
	return maxTotalPrice;
}


/***************************************************
*对应单个物品多重背包的动态规划求法(合并)
****************************************************/
void Pack::SingleMultiplePackOfMerge(Good& temp,int id,bool isFull)
{
    if(temp.GetVolume()*temp.GetNumber()>=volume)
		SingleCompletePack(temp,id,1,isFull);
    else
    {
		int k=1;
		int num=temp.GetNumber();
		while(k<num)
        {
			SingleZeroOnePack(Good(k*temp.GetVolume(),k*temp.GetPrice(),1),id,k,isFull);
			num-=k;
			k<<=1;
        }
		if(0!=num)///导致求组合数时可能出错
			SingleZeroOnePack(Good(num*temp.GetVolume(),num*temp.GetPrice(),1),id,num,isFull);
    }
}
/***************************************************
*对应所有物品多重背包的动态规划求法(合并)
****************************************************/
int Pack::SolveMultiplePackOfMerge(bool isFull)
{
	InitateDPArray(isFull);
	maxTotalPrice=0;
	for(int i=0;i<GetNumberOfGoods();i++)
		SingleMultiplePackOfMerge(GetPositionGood(i),i,isFull);
	maxTotalPrice=dp[volume];
	/*
	cout<<"总的组合数为:"<<numOfCombine[volume]<<endl;
	cout<<"其中一个组合为:"<<endl;
	PrintAllPack();//暂没找到直接好的打印方法
	PrintMultiplePack();
	*/
	SolveMultiplePackOfNotMergeOfNotOptimize(isFull);//此处打印有问题,出此下策
	return maxTotalPrice;
}
#endif



 

客户端头文件

#include<iostream>
#include<cstdio>
#include<ctime>
#include<windows.h>
#include"Good.h"
#include"Pack.h"
using namespace std;

const int MAXNPRICE=100;

void MenuFunc(int &choice,int &subChoice,int &greedySubChoice,bool& isFull,bool& isAuto)
{
	bool flag=false;
	do
	{
		if(flag)cout<<"您的选项非法,请重新输入!"<<endl;
		cout<<"+++++++++++++++++++++++主菜单+++++++++++++++++++++++"<<endl;
		cout<<"           1.01背包"<<endl;
		cout<<"           2.完全背包"<<endl;
		cout<<"           3.多重背包"<<endl;
		cout<<"           4.退出"<<endl;
		cout<<"请输入您的选项[ ]\b\b";
		cin>>choice;
		cout<<"+++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;
	}while(!(1==choice||2==choice||3==choice||4==choice));
	
	//背包问题类型
	if(1==choice)
	{
		flag=false;
		do
		{
			if(flag)cout<<"您的选项非法,请重新输入!"<<endl;
			cout<<"===============01背包子菜单================="<<endl;
			cout<<"           1.位操作"<<endl;
			cout<<"           2.DFS搜索"<<endl;
			cout<<"           3.无优化动态规划"<<endl;
			cout<<"           4.有优化动态规划"<<endl;
			cout<<"           5.遗传算法"<<endl;
			cout<<"           6.贪心算法"<<endl;
			cout<<"请输入您的选项[ ]\b\b";
			cin>>subChoice;
			cout<<"============================================="<<endl;
			flag=true;
		}while(!(1==subChoice||2==subChoice||3==subChoice||4==subChoice||5==subChoice||6==subChoice));
	}
	else if(2==choice)
	{
		flag=false;
		do
		{
			if(flag)cout<<"您的选项非法,请重新输入!"<<endl;
			cout<<"===============完全背包子菜单================="<<endl;
			cout<<"           1.无优化动态规划"<<endl;
			cout<<"           2.有优化动态规划"<<endl;
			cout<<"           3.贪心算法"<<endl;
			cout<<"请输入您的选项[ ]\b\b";
			cin>>subChoice;
			cout<<"=============================================="<<endl;
			flag=true;
		}while(!(1==subChoice||2==subChoice||3==subChoice));
	}
	else if(3==choice)
	{
		flag=false;
		do
		{
			if(flag)cout<<"您的选项非法,请重新输入!"<<endl;
			cout<<"===============多重背包子菜单================="<<endl;
			cout<<"           1.无优化不合并"<<endl;
			cout<<"           2.有优化不合并"<<endl;
			cout<<"           3.有优化合并"<<endl;
			cout<<"           4.贪心算法"<<endl;
			cout<<"请输入您的选项[ ]\b\b";
			cin>>subChoice;
			cout<<"==============================================="<<endl;
			flag=true;
		}while(!(1==subChoice||2==subChoice||3==subChoice||4==subChoice));
	}
	else exit(0);
	
    //处理是否装满
	int tmpChoice;
	if(!((1==choice&&5==subChoice)||(1==choice&&6==subChoice)//01背包遗传算法+贪心算法都为不装满
		||(2==choice&&3==subChoice)
		||(3==choice&&4==subChoice)))
	{
		flag=false;
		do
		{
			if(flag)cout<<"您的选项非法,请重新输入!"<<endl;
			cout<<"==============背包最终状态子菜单================"<<endl;
			cout<<"           1.装满"<<endl;
			cout<<"           2.不装满"<<endl;
			cout<<"请输入您的选项[ ]\b\b";
			cin>>tmpChoice;
			cout<<"==============================================="<<endl;
			flag=true;
		}while(!(1==tmpChoice||2==tmpChoice));
		if(1==tmpChoice)
			isFull=true;
		else isFull=false;
	}
	else isFull=false;
	
	//是否手动输入数据
	flag=false;
	do
	{
		if(flag)cout<<"您的选项非法,请重新输入!"<<endl;
		cout<<"===============数据产生方式子菜单================="<<endl;
		cout<<"           1.自动"<<endl;
		cout<<"           2.手动"<<endl;
		cout<<"请输入您的选项[ ]\b\b";
		cin>>tmpChoice;
		cout<<"==============================================="<<endl;
		flag=true;
	}while(!(1==tmpChoice||2==tmpChoice));
	if(1==tmpChoice)
		isAuto=true;
	else isAuto=false;
	
	//选择贪心算法
	if((1==choice&&6==subChoice)
		||(2==choice&&3==subChoice)
		||(3==choice&&4==subChoice))//贪心算法都为不装
	{
		flag=false;
		do
		{
			if(flag)cout<<"您的选项非法,请重新输入!"<<endl;
			cout<<"===============贪心策略子菜单================="<<endl;
			cout<<"           1.体积最小"<<endl;
			cout<<"           2.价值最大"<<endl;
			cout<<"           3.价值/体积最大"<<endl;
			cout<<"请输入您的选项[ ]\b\b";
			cin>>greedySubChoice;
			cout<<"==============================================="<<endl;
			flag=true;
		}while(!(1==greedySubChoice||2==greedySubChoice||3==greedySubChoice));
	}
	
}


void Process()
{
	int nunOfGood,pri,vol,num,maxVolOfPack;
	int choice,subChoice,greedySubChoice;
	bool isFull,isAuto;
	Pack myPack;
	MenuFunc(choice,subChoice,greedySubChoice,isFull,isAuto);
	if(isAuto)
	{
		srand(unsigned(clock()));
		cout<<"请输入背包最大容量:";
		maxVolOfPack=rand()%MAXVOLUME;
		cout<<maxVolOfPack<<endl;
		cout<<"请输入物品数量:";
		nunOfGood=rand()%MAXN;
		cout<<nunOfGood<<endl;
		
		myPack.SetPack(maxVolOfPack);//实例化背包类
		
		int i=1;
		while(i<=nunOfGood)
		{
			cout<<"第"<<i<<"组物品:";
			do
			{
				vol=rand()%(maxVolOfPack+1);
			}while(0==vol);
			pri=rand()%MAXNPRICE;
			if(3==choice)
			{
				num=rand()%10;/
				cout<<"("<<vol<<","<<pri<<","<<num<<")"<<endl;
			}
			else 
			{
				num=1;随便赋值
				cout<<"("<<vol<<","<<pri<<")"<<endl;
			}
			
			myPack.PushBack(myPack.GetVector(),Good(vol,pri,num));
			++i;
		}
	}
	else
	{
		bool isInpoutCor=false;
		do
		{
			if(isInpoutCor)
				cout<<"您的容量过大,请重新输入!"<<endl;
			cout<<"请输入背包最大容量:"<<endl;
			isInpoutCor=true;
			cin>>maxVolOfPack;
		}while(maxVolOfPack>=MAXVOLUME);
		myPack.SetPack(maxVolOfPack);//实例化背包类
		
		isInpoutCor=false;
		do
		{
			if(isInpoutCor)
				cout<<"您的物品数量过大,请重新输入!"<<endl;
			cout<<"请输入物品数量:"<<endl;
			isInpoutCor=true;
			cin>>nunOfGood;
		}while(nunOfGood>=MAXN);
		int i=1;
		while(i<=nunOfGood)
		{
			cout<<"第"<<i<<"组物品:";
			cin>>vol>>pri;
			if(3==choice)
				cin>>num;
			else num=1;随便赋值
			myPack.PushBack(myPack.GetVector(),Good(vol,pri,num));
			++i;
		}
	}
	
	FILETIME beg,end;

	GetSystemTimeAsFileTime(&beg);
	if(1==choice)
	{
		if(1==subChoice)
			cout<<"位操作枚举01背包最大价值:"<<myPack.BitOperationEnum(isFull)<<endl<<endl;
		else if(2==subChoice)
			cout<<"DFS枚举01背包最大价值:"<<myPack.searchEnum(isFull)<<endl<<endl;	
		else if(3==subChoice)
			cout<<"无优化的动态规划01背包最大价值:"<<myPack.SolveZeroOnePackOfNotOptimize(isFull)<<endl<<endl;
		else if(4==subChoice)
			cout<<"优化的动态规划01背包最大价值:"<<myPack.SolveZeroOnePack(isFull)<<endl<<endl;
		else if(5==subChoice)
			myPack.SetPackPopulation(maxVolOfPack,1000,nunOfGood,0.05,0.3);
		else if(6==subChoice)
		{
			if(1==greedySubChoice)
				cout<<"贪心策略体积最小01背包最大价值:"<<myPack.GreedOfGetMinVolume(false)<<endl<<endl;
			else if(2==greedySubChoice)
				cout<<"贪心策略价值最大01背包最大价值:"<<myPack.GreedOfGetMaxPrice(false)<<endl<<endl;
			else if(3==greedySubChoice)
				cout<<"贪心策略价值/体积最大01背包最大价值:"<<myPack.GreedOfGetMaxDensity(false)<<endl<<endl;
		}
	}
	else if(2==choice)
	{
		if(1==subChoice)
			cout<<"无优化的动态规划完全背包最大价值:"<<myPack.SolveCompletePackOfNotOptimize(isFull)<<endl<<endl;	
		else if(2==subChoice)
			cout<<"优化的动态规划完全背包最大价值:"<<myPack.SolveCompletePack(isFull)<<endl<<endl;
		else if(3==subChoice)
		{
			if(1==greedySubChoice)
				cout<<"贪心策略体积最小完全背包最大价值:"<<myPack.GreedOfGetMinVolume(true)<<endl<<endl;
			else if(2==greedySubChoice)
				cout<<"贪心策略价值最大完全背包最大价值:"<<myPack.GreedOfGetMaxPrice(true)<<endl<<endl;
			else if(3==greedySubChoice)
				cout<<"贪心策略价值/体积最大完全背包最大价值:"<<myPack.GreedOfGetMaxDensity(true)<<endl<<endl;
		}
	}
	else if(3==choice)
	{
		if(1==subChoice)
			cout<<"无优化的动态规划多重背包最大价值(不合并):"<<myPack.SolveMultiplePackOfNotMergeOfNotOptimize(isFull)<<endl<<endl;
		else if(2==subChoice)
		{
			cout<<"优化的动态规划多重背包最大价值(不合并):"<<myPack.SolveMultiplePackOfNotMerge(isFull)<<endl<<endl;//统计组合数有问题
		}
		else if(3==subChoice)
		{
			cout<<"动态规划多重背包最大价值(合并):"<<myPack.SolveMultiplePackOfMerge(isFull)<<endl<<endl;
		}
		else if(4==subChoice)
		{
			if(1==greedySubChoice)
				cout<<"贪心策略体积最小多重背包最大价值:"<<myPack.GreedOfGetMinVolume(false)<<endl<<endl;
			else if(2==greedySubChoice)
				cout<<"贪心策略价值最大多重背包最大价值:"<<myPack.GreedOfGetMaxPrice(false)<<endl<<endl;
			else if(3==greedySubChoice)
				cout<<"贪心策略价值/体积最大多重背包最大价值:"<<myPack.GreedOfGetMaxDensity(false)<<endl<<endl;
		}
	}
	GetSystemTimeAsFileTime(&end);
	double dur=(end.dwLowDateTime-beg.dwLowDateTime);
	cout<<"运行耗时"<<dur<<endl;//1*10*1000*1000分之1秒
	cout<<endl<<endl;
}


 

程序入口

#include<iostream>
#include<cstdio>
#include<ctime>
#include"Good.h"
#include"Pack.h"
#include"Client.h"
using namespace std;


int main()
{
	while(true)
		Process();
	return 0;
}


      以上是我的报告及源代码,欢迎斧正!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值