算法分析与程序设计动态规划及回溯法解01背包问题Word文档下载推荐.docx
《算法分析与程序设计动态规划及回溯法解01背包问题Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《算法分析与程序设计动态规划及回溯法解01背包问题Word文档下载推荐.docx(11页珍藏版)》请在冰豆网上搜索。
求出获得最大价值的方案。
2.回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标。
但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。
当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。
(其实回溯法就是对隐式图的深度优先搜索算法)。
若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。
而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束
可用回溯法求解的问题P,通常要能表达为:
对于已知的由n元组(x1,x2,…,xn)组成的一个状态空间E={(x1,x2,…,xn)∣xi∈Si,i=1,2,…,n},给定关于n元组中的一个分量的一个约束集D,要求E中满足D的全部约束条件的所有n元组。
其中Si是分量xi的定义域,且|Si|有限,i=1,2,…,n。
我们称E中满足D的全部约束条件的任一n元组为问题P的一个解。
解问题P的最朴素的方法就是枚举法,即对E中的所有n元组逐一地检测其是否满足D的全部约束,若满足,则为问题P的一个解。
但显然,其计算量是相当大的。
我们发现,对于许多问题,所给定的约束集D具有完备性,即i元组(x1,x2,…,xi)满足D中仅涉及到x1,x2,…,xi的所有约束意味着j(j<
=i)元组(x1,x2,…,xj)一定也满足D中仅涉及到x1,x2,…,xj的所有约束,i=1,2,…,n。
换句话说,只要存在0≤j≤n-1,使得(x1,x2,…,xj)违反D中仅涉及到x1,x2,…,xj的约束之一,则以(x1,x2,…,xj)为前缀的任何n元组(x1,x2,…,xj,xj+1,…,xn)一定也违反D中仅涉及到x1,x2,…,xi的一个约束,n≥i≥j。
因此,对于约束集D具有完备性的问题P,一旦检测断定某个j元组(x1,x2,…,xj)违反D中仅涉及x1,x2,…,xj的一个约束,就可以肯定,以(x1,x2,…,xj)为前缀的任何n元组(x1,x2,…,xj,xj+1,…,xn)都不会是问题P的解,因而就不必去搜索它们、检测它们。
回溯法正是针对这类问题,利用这类问题的上述性质而提出来的比枚举法效率更高的算法。
二、算法设计(或算法步骤)
1.这是最基础的背包问题,特点是:
每种物品仅有一件,可以选择放或不放。
用子问题定义状态:
即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。
则其状态转移方程便是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。
所以有必要将它详细解释一下:
“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。
如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”,价值为f[i-1][v];
如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。
2.
(1)
分析要解决的问题,给出你的思路,可以借助图表等辅助表达。
01背包问题用回溯法实现就是要枚举其所有的解空间,时间复杂度为
(2)nO左右。
搜索的具体方法如下:
对于每一个物品i,对于该物品只有选与不选2个决策,总共有n个物品,可以顺序依次考虑每个物品,这样就形成了一棵解空间树:
基本思想就是遍历这棵树,以枚举所有情况,最后进行判断,如果重量不超过背包容量,且价值最大的话,该方案就是最后的答案。
利用回溯算法写还可以加入一些优化,进行剪枝,因为很多情况是没有意义的,例如当重量大于背包容量的时候,没有必要对剩下的物品再来决策了。
或者将剩下的所有物品都选取其总价值也没有目前已经求得的方案的价值还大的话,也是可以剪枝的。
(2)分析利用你的想法解决该问题可能会有怎样的时空复杂度。
时间复杂度估计:
(2)nO
因为物品只有选与不选2个决策,而总共有n个物品,所以时间复杂度为
(2)nO。
空间复杂度估计:
()On
因为递归栈最多达到n层,而且存储所有物品的信息也只需要常数个一维数组,所以最终的空间复杂度为()On。
三、算法实现
1.动态规划
#include<
stdio.h>
stdlib.h>
intc[50][50];
intw[10],v[10];
intx[10];
intn;
voidKNAPSACK_DP(intn,intW);
voidOUTPUT_SACK(intc[50][50],intk);
voidKNAPSACK_DP(intn,intW)
{
inti,k;
for(k=0;
k<
=W;
k++)
c[0][k]=0;
for(i=1;
i<
=n;
i++)
{
c[i][0]=0;
for(k=1;
{
if(w[i]<
=k)
{
if(v[i]+c[i-1][k-w[i]]>
c[i-1][k])
c[i][k]=v[i]+c[i-1][k-w[i]];
else
c[i][k]=c[i-1][k];
}
else
c[i][k]=c[i-1][k];
}
}
}
voidOUTPUT_SACK(intc[50][50],intk)
inti;
for(i=n;
i>
=2;
i--)
if(c[i][k]==c[i-1][k])
x[i]=0;
else
x[i]=1;
k=k-w[i];
x[1]=(c[1][k]?
1:
0);
printf("
%4d"
x[i]);
voidmain()
intm;
inti,j,k;
输入物品个数:
"
);
scanf("
%d"
&
n);
依次输入物品的重量:
\n"
scanf("
w[i]);
依次输入物品的价值:
v[i]);
输入背包最大容量:
m);
=m;
printf("
i);
KNAPSACK_DP(n,m);
构造最优解过程如下:
for(j=1;
j<
=5;
j++)
printf("
c[j][k]);
最优解为:
OUTPUT_SACK(c,m);
system("
pause"
}
2.回溯法
#include<
malloc.h>
windows.h>
typedefstructgoods
{
double*value;
//价值
double*weight;
//重量
char*select;
//是否选中到方案
intnum;
//物品数量
doublelimitw;
//限制重量
}GOODS;
doublemaxvalue,totalvalue;
char*select1;
voidbackpack(GOODS*g,inti,doubletw,doubletv)
intk;
if(tw+g->
weight[i]<
=g->
limitw)
{
select1[i]=1;
if(i<
g->
num-1)
backpack(g,i+1,tw+g->
weight[i],tv);
for(k=0;
k<
num;
++k)
select[k]=select1[k];
maxvalue=tv;
}
select1[i]=0;
if(tv-g->
value[i]>
maxvalue)
backpack(g,i+1,tw,tv-g->
value[i]);
for(k=0;
g->
maxvalue=tv-g->
value[i];
intmain()
doublesumweight;
GOODSg;
背包最大重量:
%lf"
g.limitw);
可选物品数量:
g.num);
if(!
(g.value=(double*)malloc(sizeof(double)*g.num)))
内存分配失败\n"
exit(0);
(g.weight=(double*)malloc(sizeof(double)*g.num)))
(g.select=(char*)malloc(sizeof(char)*g.num)))
(select1=(char*)malloc(sizeof(char)*g.num)))
totalvalue=0;
for(i=0;
i<
g.num;
i++)
输入第%d号物品的重量和价值:
i+1);
%lf%lf"
g.weight[i],&
g.value[i]);
totalvalue+=g.value[i];
\n背包最大能装的重量为:
%.2f\n\n"
g.limitw);
第%d号物品重:
%.2f,价值:
%.2f\n"
i+1,g.weight[i],g.value[i]);
i++)
select1[i]=0;
maxvalue=0;
backpack(&
g,0,0.0,totalvalue);
sumweight=0;
\n可将以下物品装入背包,使背包装的物品价值最大:
++i)
if(g.select[i])
第%d号物品,重量:
sumweight+=g.weight[i];
\n总重量为:
%.2f,总价值为:
sumweight,maxvalue);
return0;
四、算法分析(与改进)
(1)
算法实现的复杂度在问题规模很大时可以接受吗?
答:
不可以接受。
因为该算法是指数级别的时间复杂度为(*2)nOn,当n较大时,也能在一定时间内无法得出结果。
空间复杂度为()On,还可以接受。
但是综合上面分析,时间复杂度成为极大地瓶颈。
所以规模很大时不可以接受。
(2)所选用的数据结构合适吗?
合适,只用到一维数组。
使用的数据结构简单,易理解。
能够对数组中的每个元素随机访问。
(3)
该算法都存在哪几类可能出现的情况,你的测试完全覆盖了你所想到的这些
情况吗,测试结果如何?
测试全面。
输入规模为0时,程序自动结束。
输入总重量小于背包容量时,结果为所有物品的价值总和。
输入的总重量大于背包容量时,结果为能装入所有方案中最大的值。
(4)
叙述通过实验你对回溯法方法的理解及其优缺点
优点:
回溯法在普通的深度优先搜索的基础上增加了限界函数,对不必要的解空间树进行剪枝,使得可行解的数量大大减少,增加了搜索速度。
回溯法的设计也非常简单,即简单的枚举搜索策略,只需要分析细节过程就能增加剪枝的操作。
另外,空间复杂度通常非常小,只有搜索深度左右的空间。
缺点:
回溯法虽然设计简单,但是时间复杂度非常高,通常是指数级别的。
而且回溯法的难点在于限界函数的设计。
而且回溯法通常需要遍历完所有的解空间才能得出最优值,而不是像宽度优先搜索一样第一次求出的值便是最优值。
报告成绩单
评
语
成绩