时间复杂度为O(n).
贪心法:
算法思想:
贪心原则为单位价值最大且重量最小,不超过背包最大承重量为约束条件。
也就是说,存在单位重量价值相等的两个包,则选取重量较小的那个背包。
但是,贪心法当在只有在解决物品可以分割的背包问题时是正确的。
贪心算法总是作出在当前看来最好的选择。
也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
用贪心法设计算法的特点是一步一步地进行,根据某个优化测度(可能是目标函数,也可能不是目标函数),每一步上都要保证能获得局部最优解。
每一步只考虑一个数据,它的选取应满足局部优化条件。
若下一个数据与部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,
直到把所有数据枚举完,或者不能再添加为止。
回溯法:
回溯法:
为了避免生成那些不可能产生最佳解的问题状态,要不断地利用限界函数(boundingfunction)来处死那些实际上不可能产生所需解的活结点,以减少问题的计算量。
这种具有限界函数的深度优先生成法称为回溯法。
对于有n种可选物品的0/1背包问题,其解空间由长度为n的0-1向量组成,可用子集数表示。
在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入左子树。
当右子树中有可能包含最优解时就进入右子树搜索。
时间复杂度为:
O(2n)
空间复杂度为:
O(n)
分支限界算法:
首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。
在优先队列分支限界法中,节点的优先级由已装袋的物品价值加上剩下的最大单位重量价值的物品装满剩余容量的价值和。
算法首先检查当前扩展结点的左儿子结点的可行性。
如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。
当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。
当扩展到叶节点时为问题的最优值。
3.设计并实现所设计的算法。
4.对比不同算法求解该问题的优劣。
这动态规划算法和贪心算法是用来分别解决不同类型的背包问题的,当一件背包物品可以分割的时候,使用贪心算法,按物品的单位体积的价值排序,从大到小取即可。
当一件背包物品不可分割的时候,(因为不可分割,所以就算按物品的单位体积的价值大的先取也不一定是最优解)此时使用贪心是不对的,应使用动态规划。
设计方法
时间复杂度
优点
缺点
动态规划
Min{nc,2n}
可求得最优决策序列
速度慢
贪心方法
O(2n)
速度较快
很难得到最优解
回溯法
O(n2n)
能够得到最优解
时间复杂度较高
分支限界法
O(2n)
速度较快,易求解
占用内存大,效率不高
5.需要提交不同算法的实现代码和总结报告。
动态规划方法:
publicclassKnapsack{
publicstaticvoidmain(String[]args){
int[]value={0,60,100,120};
int[]weigh={0,10,20,30};
intweight=50;
Knapsack1(weight,value,weigh);
}
publicstaticvoidKnapsack1(intweight,int[]value,int[]weigh){
int[]v=newint[value.length];
int[]w=newint[weigh.length];
int[][]c=newint[value.length][weight+1];
intd[]=newint[100];
for(inti=0;iv[i]=value[i];
w[i]=weigh[i];
}
for(inti=1;ifor(intk=1;k<=weight;k++){
if(w[i]<=k){
c[i][k]=max(c[i-1][k],c[i-1][k-w[i]]+v[i]);
}else{
c[i][k]=c[i-1][k];
}
}
}
System.out.println(c[value.length-1][weight]);
}
privatestaticintmax(inti,intj){
intk=i>j?
i:
j;
returnk;
}
}
贪心法:
publicclassGreedyKnapSack{
publicstaticvoidmain(String[]args){
int[]value={0,60,100,120};
int[]weigh={0,10,20,30};
intweight=50;
Knapsack1(weight,value,weigh);
}
privatestaticvoidKnapsack1(intweight,int[]v,int[]w){
intn=v.length;
double[]r=newdouble[n];
int[]index=newint[n];
for(inti=0;ir[i]=(double)v[i]/(double)w[i];
index[i]=i;
}
//按单位重量价值r[i]=v[i]/w[i]降序排列
doubletemp=0;
for(inti=0;ifor(intj=i+1;jif(r[i]temp=r[i];
r[i]=r[j];
r[j]=temp;
intx=index[i];
index[i]=index[j];
index[j]=x;
}
}
}
//排序后的重量和价值分别存到w1[]和v1[]中
int[]w1=newint[n];
int[]v1=newint[n];
for(inti=0;iw1[i]=w[index[i]];
v1[i]=v[index[i]];
}
System.out.println(Arrays.toString(w1));
System.out.println(Arrays.toString(v1));
ints=0;//包内现存货品的重量
intvalue=0;//包内现存货品总价值
for(inti=0;iif(s+w1[i]value+=v1[i];
s+=w1[i];
}
}
System.out.println("背包中物品的最大总价值为"+value);
}
}
回溯法:
publicclassBacktrackKnapSack{
publicstaticvoidmain(String[]args){
int[]value={0,60,100,120};
int[]weigh={0,10,20,30};
intweight=50;
Knapsack1(weight,value,weigh);
}
privatestaticvoidKnapsack1(intweight,int[]v,int[]w){
intn=v.length;
double[]r=newdouble[n];
int[]index=newint[n];
for(inti=0;ir[i]=(double)v[i]/(double)w[i];
index[i]=i;
}
//按单位重量价值r[i]=v[i]/w[i]降序排列
doubletemp=0;
for(inti=0;ifor(intj=i+1;jif(r[i]temp=r[i];
r[i]=r[j];
r[j]=temp;
intx=index[i];
index[i]=index[j];
index[j]=x;
}
}
}
//排序后的重量和价值分别存到w1[]和v1[]中
int[]w1=newint[n];
int[]v1=newint[n];
for(inti=0;iw1[i]=w[index[i]];
v1[i]=v[index[i]];
}
//调用函数KnapSackBackTrack(),输出打印装完物品以后的最大价值
KnapSackBackTrack(w1,v1,w1.length,weight);
}
privatestaticvoidKnapSackBackTrack(int[]w1,int[]v1,intlength,
intweight){
intCurrentWeight=0;
intCurrentValue=0;
intmaxValue=0;
inti=0;
intn=v1.length;
while(i>=0){
if(CurrentWeight+w1[i]CurrentWeight+=w1[i];
CurrentValue+=v1[i];
i++;
}
else
break;
}
if(i