算法设计与分析 普通背包 01背包 棋盘覆盖 课程设计.docx
《算法设计与分析 普通背包 01背包 棋盘覆盖 课程设计.docx》由会员分享,可在线阅读,更多相关《算法设计与分析 普通背包 01背包 棋盘覆盖 课程设计.docx(21页珍藏版)》请在冰豆网上搜索。
算法设计与分析普通背包01背包棋盘覆盖课程设计
课程设计报告
课程设计名称:
算法设计与分析
系:
三系
学生姓名:
班级:
软件
(2)班
学号:
201103112
成绩:
指导教师:
秦川刘玉龙
开课时间:
2013学年2学期
一、问题描述
1.普通背包装载问题(贪心算法)
给定n种物品和一个背包。
物品i的重量是w[i],其价值是p[i],背包的容量是C。
应如何选择装入背包的物品,是得装入背包中的物品的总价值最大?
在选择装入背包的物品是,对每种物品i的选择可以是物品的的一部分(即可以不是整数),而不一定要全部装入背包,1<=i<=n.
2.0-1背包装载问题(回溯算法)
对于0-1背包问题回溯法一个实例,n=4,c=7,p=[9,10,7,4],w=[3,5,2,1].这4个物品的单位重量价值分别为[3,2,3.5,4].以物品为单位价值的递减序装入物品。
先装入物品4,然后装入物品3和1.装入这3个物品后,剩余的背包容量为1,只能装入0.2的物品2.由此可得到一个解为x=[1,0,2,1,1],其相应的价值为22.尽管这不是一个可行解,但可以证明其价值是最有大的上界。
因此,对于这个实例,最优值不超过22.
回溯法的基本思想是按深度优先策略,从根节点出发搜索解空间树,算法搜索至解空间的任一点时,先判断该结点是否包含问题的解,如果肯定不包含,则跳过以该结点为根的子树的搜索,逐层向其祖先结点回溯,否则,进入该子树,继续按深度优先进行搜索。
3.棋盘覆盖问题(分治算法)
在一个(2^k)×(2^k)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为特殊方格。
显然,特殊方格在棋盘中出现的位置有4^k情形,因而有4^k种不同的棋盘。
图1中a所示是k=2时16种棋盘中的一个。
棋盘覆盖问题要求用图1中b所示的4种不同形状的L型骨牌覆盖给定棋盘上除特殊棋盘方格以外的所有方格,且任何两个L型骨牌不得重叠覆盖。
图1棋盘覆盖骨牌
二、问题分析
1.普通背包装载问题(贪心算法)
贪心算法的基本思路:
从问题的某一个初始解出发逐步逼近给定的目标,每一步都作一个不可回溯的决策,尽可能地求得最好的解。
当达到某算法中的某一步不需要再继续前进时,算法停止。
这是最基础的背包问题,特点是:
每种物品仅有一件,可以选择放或不放。
贪心算法解决背包问题有几种策略:
(1)一种贪婪准则为:
从剩余的物品中,选出可以装入背包的价值最大的物品,利用这种规则,价值最大的物品首先被装入,然后是下一个价值最大的物品,如继续下去。
这种策略不能保证得到最优解。
例如,考虑n=2,w=[100,10,10],p=[20,15,15],c=105。
当利用价值贪婪准则时,获得的解为x=[1,0,0],这种方案的总价值为2 0。
而最优解为[0,1,1],其总价值为30。
(2)另一种方案是重量贪婪准则是:
从剩下的物品中选择可装入背包的重量最小的物品。
虽然这种规则对于前面的例子能产生最优解,但在一般情况下则不一定能得到最优解。
考虑n=2,w=[10,20],p=[5,100],c=25。
当利用重量贪婪策略时,获得的解为x=[1,0],比最优解[0,1]要差。
(3)还有一种贪婪准则,就是我们教材上提到的,认为,每一项计算yi=vi/si,即该项值和大小的比,再按比值的降序来排序,从第一项开始装背包,然后是第二项,依次类推,尽可能的多放,直到装满背包。
2.0-1背包装载问题(回溯算法)
利用回溯法的设计思想来解决背包问题。
首先是将可供选择的物品的个数输入程序,将物品排成一列,计算总物品的体积s,然后输入背包的实际体积V,如果背包的体积小于0或者大于物品的总体积s,则判断输入的背包体积错误,否则开始顺序选取物品装入背包,假设已选取了前i件物品之后背包还没有装满,则继续选取第i+1件物品,若该件物品"太大"不能装入,则弃之而继续选取下一件,直至背包装满为止。
但如果在剩余的物品中找不到合适的物品以填满背包,则说明"刚刚"装入背包的那件物品"不合适",应将它取出"弃之一边",继续再从"它之后"的物品中选取,如此重复,直至求得满足条件的解。
因为回溯求解的规则是"后进先出",所以要用到栈来存储符合条件的解,在存储过程中,利用数组来存储各个物品的体积,然后用深度优先的搜索方式求解,将符合条件的数组元素的下标存入栈里,最后得到符合条件的解并且实现输出。
3.棋盘覆盖问题(分治算法)
分治的技巧在于如何划分棋盘,使划分后的子棋盘的大小相同,并且每个子棋盘均包含一个特殊方格,从而将原问题分解为规模较小的棋盘覆盖问题。
先把原始棋盘划分成4个相等的棋盘,由于棋盘只有一个特殊棋盘,所以这4个子棋盘中只有一个子棋盘包含该特殊棋盘,以便采用递归的方法求解,可以用1一个L型骨牌覆盖这3个较小棋盘的汇合处。
从而将原问题转换为4个较小规模的棋盘覆盖问题。
递归使用这种划分策略,直至将棋盘分割为1*1的子棋盘。
将2^k x 2^k的棋盘,先分成相等的四块子棋盘,其中特殊方格位于四个中的一个,构造剩下没特殊方格三个子棋盘,将他们中的也假一个方格设为特殊方格。
如果是:
左上的子棋盘(若不存在特殊方格)则将该子棋盘右下角的那个方格假设为特殊方格
右上的子棋盘(若不存在特殊方格)则将该子棋盘左下角的那个方格假设为特殊方格
左下的子棋盘(若不存在特殊方格)则将该子棋盘右上角的那个方格假设为特殊方格
右下的子棋盘(若不存在特殊方格)则将该子棋盘左上角的那个方格假设为特殊方格
当然上面四种,只可能且必定只有三个成立,那三个假设的特殊方格刚好构成一个L型骨架,我们可以给它们作上相同的标记。
这样四个子棋盘就分别都和原来的大棋盘类似,我们就可以用递归算法解决。
3、建立数学模型
1.普通背包问题
给定背包容量C>0,第i(00,价值Vi>0,要求找出一个n元向量(X1,X2,.....,Xn),0<=Xi<=1,使得
<=C,而且
达到最大。
2.0-1背包问题
给定背包容量C>0,第i(00,价值Vi>0,要求找出一个n元0-1向量(X1,X2,.....,Xn),Xi为0或1,使得
<=C,而且
达到最大。
3.棋盘覆盖问题
(1)棋盘:
可以一个二维数组board[size][size]表示一个棋盘,其中,size=2^k。
为了在递归处理的过程中使用同一个棋盘,将数组board设置为全局变量
(2)子棋盘:
子棋盘由原始棋盘数组board的行下标tr,列下标tc表示。
(3)特殊方格:
用board[dr][dc]表示特殊方格,dr和dc表示该特殊方格的在二维数组board中的下标
(4)L型骨牌:
一个(2^k)*(2^k)的棋盘中有一个特殊方格,所以用到L型骨牌的个数为(4^k-1)/3,将所有L型骨牌从1开始连续编号,同一个骨牌有3个方格组成,这3个方格用同一个编号。
四、算法设计
1.普通背包装载问题(贪心算法)
普通背包问题恰好符合贪心算法解决问题的特点:
(1)贪心选择性(问题整体最优解可以通过一系列局部最优的选择来达到。
即,通过一系列的逐步贪心选择使得最终的选择方案是全局最优)
(2)最优子结构(原问题的最优解能够包含其子问题的最优解)
因此对于该问题可以用贪心算法解决。
算法设计:
首先,按物品单位价值由大到小将物品排序;(为了贪心选择准备)然后,依次将单位价值大的物品放入背包(贪心选择),直到背包放满为止;在向背包放置物品的过程中,如果正在考虑装入的物品不能全部放进去,则可以将物品的部分放入背包;
2.0-1背包装载问题(回溯算法)
使用栈作为该程序的数据结构,利用栈进行语法检查,以深度优先的搜索方式解空间,实现递归过程和函数的调用,在设计时还使用C语言的数组及其循环语言来实现程序。
运用回溯法解题,在搜索解空间树时,只要其左子节点是一个可行结点,搜索就进入左子树,在右子树中有可能包含最优解是才进入右子树搜索。
否则将右子树剪去。
具体步骤如下:
(1)针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结;
(3)以深度优先的方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
3.棋盘覆盖问题
当k>0时,将2^k×2^k棋盘分割为4个2^k-1×2^k-1子棋盘。
特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。
为了将这3个无特殊方格的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,从而将原问题转化为4个较小规模的棋盘覆盖问题。
递归地使用这种分割,直至棋盘简化为棋盘2×2。
因此应使用分治算法。
算法基本思想:
将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
五、算法实现
1.普通背包装载问题(贪心算法)
voidsort(objectinfoobject[],intn)//冒泡排序法将单位重量价值降序排列
{
inti,j;
floattemp;
for(i=0;i{
object[i].a=object[i].v/object[i].w;
}
for(i=0;i{
for(j=0;j{
if(object[j].a