背包问题的一些想法

来源:互联网 发布:think in java 英文版 编辑:程序博客网 时间:2024/05/19 14:37

原创作品,出自 “晓风残月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;}elsememset(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;}elsememset(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;}


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

0 0