原创作品,出自 “晓风残月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;
}
以上是我的报告及源代码,欢迎斧正!