算法分析与设计课程设计报告.docx

上传人:b****1 文档编号:1690051 上传时间:2023-05-01 格式:DOCX 页数:35 大小:194.58KB
下载 相关 举报
算法分析与设计课程设计报告.docx_第1页
第1页 / 共35页
算法分析与设计课程设计报告.docx_第2页
第2页 / 共35页
算法分析与设计课程设计报告.docx_第3页
第3页 / 共35页
算法分析与设计课程设计报告.docx_第4页
第4页 / 共35页
算法分析与设计课程设计报告.docx_第5页
第5页 / 共35页
算法分析与设计课程设计报告.docx_第6页
第6页 / 共35页
算法分析与设计课程设计报告.docx_第7页
第7页 / 共35页
算法分析与设计课程设计报告.docx_第8页
第8页 / 共35页
算法分析与设计课程设计报告.docx_第9页
第9页 / 共35页
算法分析与设计课程设计报告.docx_第10页
第10页 / 共35页
算法分析与设计课程设计报告.docx_第11页
第11页 / 共35页
算法分析与设计课程设计报告.docx_第12页
第12页 / 共35页
算法分析与设计课程设计报告.docx_第13页
第13页 / 共35页
算法分析与设计课程设计报告.docx_第14页
第14页 / 共35页
算法分析与设计课程设计报告.docx_第15页
第15页 / 共35页
算法分析与设计课程设计报告.docx_第16页
第16页 / 共35页
算法分析与设计课程设计报告.docx_第17页
第17页 / 共35页
算法分析与设计课程设计报告.docx_第18页
第18页 / 共35页
算法分析与设计课程设计报告.docx_第19页
第19页 / 共35页
算法分析与设计课程设计报告.docx_第20页
第20页 / 共35页
亲,该文档总共35页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

算法分析与设计课程设计报告.docx

《算法分析与设计课程设计报告.docx》由会员分享,可在线阅读,更多相关《算法分析与设计课程设计报告.docx(35页珍藏版)》请在冰点文库上搜索。

算法分析与设计课程设计报告.docx

算法分析与设计课程设计报告

 

一、问题描述

1、普通背包问题

有一个背包容量为C,输入N个物品,每个物品有重量S[i],以及物品放入背包中所得的收益P[i]。

求选择放入的物品,不超过背包的容量,且得到的收益最好。

物品可以拆分,利用贪心算法解决。

2、0-1背包问题

在0/1背包问题中,需对容量为c的背包进行装载。

从n个物品中选取装入背包的物品,每件物品i的重量为wi,价值为pi。

对于可行的背包装载,背包中的物品的总重量不能超过背包的容量,最佳装载是指所装入的物品价值最高,

即取得最大值。

约束条件<=c和。

在这个表达式中,需求出xi的值。

xi=1表示物品i装入背包中,xi=0表示物品i不装入背包。

3、棋盘覆盖问题

在一个2k×2k个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。

在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。

 

二、问题分析

1、普通背包问题

贪心算法的基本思想是:

从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的求得更好的解。

当达到算法中的某一步不能再继续前进时,算法停止。

背包问题用贪心算法来解决,先求出每件物品的平均价值即p[i]/s[i],然后每次选择利润p[i]/s[i]最大的物品装进背包,这样就使得目标函数增加最快,当最后一种物品放不下时,选择一个适当的拆分,使物品装满背包,使得总的价值最大。

2、0-1背包问题

回溯法:

是一个既带有系统性又带有跳跃性的的搜索算法。

其基本思想是在搜索尝试中找问题的解,当不满足条件就”回溯”返回,尝试别的路径。

它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。

算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。

如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其原先结点回溯。

否则,进入该子树,继续按深度优先的策略进行搜索。

利用回溯算法来解决0-1背包,首先是将可供选择的物品的个数输入程序,将物品排成一列,计算总物品的重量weight,然后输入背包的实际重量weight,如果背包的重量小于0或者大于物品的总重量weight,则判断输入的背包重量错误,否则开始顺序选取物品装入背包,假设已选取了前i件物品之后背包还没有装满,则继续选取第i+1件物品,若该件物品"太大"不能装入,则弃之而继续选取下一件,直至背包装满为止。

但如果在剩余的物品中找不到合适的物品以填满背包,则说明"刚刚"装入背包的那件物品"不合适",应将它取出"弃之一边",继续再从"它之后"的物品中选取,如此重复,直至求得满足条件的解。

因为回溯求解的规则是"后进先出",所以要用到栈来存储符合条件的解,在存储过程中,利用数组来存储各个物品的体积,然后用深度优先的搜索方式求解,将符合条件的数组元素的下标存入栈里,最后得到符合条件的解并且实现输出。

3、棋盘覆盖问题

将2^kx2^k的棋盘,先分成相等的四块子棋盘,其中特殊方格位于四个中的一个,构造剩下没特殊方格三个子棋盘,将他们中的也假一个方格设为特殊方格。

如果是:

左上的子棋盘(若不存在特殊方格)则将该子棋盘右下角的那个方格假设为特殊方格;

右上的子棋盘(若不存在特殊方格)则将该子棋盘左下角的那个方格假设为特殊方格;

左下的子棋盘(若不存在特殊方格)则将该子棋盘右上角的那个方格假设为特殊方格;

右下的子棋盘(若不存在特殊方格)则将该子棋盘左上角的那个方格假设为特殊方格。

当然上面四种,只可能且必定只有三个成立,那三个假设的特殊方格刚好构成一个L型骨架,我们可以给它们作上相同的标记。

这样四个子棋盘就分别都和原来的大棋盘类似,我们就可以用递归算法解决。

三、算法设计

1、普通背包问题

1)基本思路:

首先,按物品单位价值由大到小将物品排序;(为了贪心选择准备)

然后,依次将单位价值大的物品放入背包(贪心选择),直到背包放满为止;

在向背包放置物品的过程中,如果正在考虑装入的物品不能全部放进去,则可以将物品的部分放入背包;

2)算法设计:

用一维数组x[n](x[i]∈{1,2,…,n}),保存问题的解;weight[]存储物品重量;price[]存储物品价值。

2、0-1背包问题

1)基本思路:

①确定问题的解空间,并将解空间组织成易于搜索的子集树的形式;

图4.1解空间树

②以深度优先的方式搜索整个解空间,遇到不满足约束要求的节点就回溯,沿另一个分支继续搜索;约束函数剪枝,不能超载,超载就回溯。

3、棋盘覆盖问题

1)问题分解过程如下:

以k=2时的问题为例,用二分法进行分解,得到的是如下图4-8,用双线划分的四个k=1的棋盘。

但要注意这四个棋盘,并不都是与原问题相似且独立的子问题。

因为当如图4-8中的残缺方格在左上部时,第1个子问题与原问题相似,而右上角、左下角和右下角三个子棋盘(也就是图中标识为2、3、4号子棋盘),并不是原问题的相似子问题,自然也就不能独立求解了。

当使用一个①号三格板(图中阴影)覆盖2、3、4号三个子棋盘的各一个方格后,如4-8右图所示,我们把覆盖后的方格,也看作是残缺方格(称为“伪”残缺方格),这时的2、3、4号子问题就是独立且与原问题相似的子问题了。

图4.2图4.3

从以上例子还可以发现,当残缺方格在第1个子棋盘,用①号三格板覆盖其余三个子棋盘的交界方格,可以使另外三个子棋盘转化为独立子问题;同样地(如下图4-9所示),当残缺方格在第2个子棋盘时,则首先用②号三格板进行棋盘覆盖,当残缺方格在第3个子棋盘时,则首先用③号三格板进行棋盘覆盖,当残缺方格在第4个子棋盘时,则首先用④号三格板进行棋盘覆盖,这样就使另外三个子棋盘转化为独立子问题。

如下图4.4所示:

图4.4

2)棋盘的识别:

棋盘的规模是一个必要的信息,有了这个信息,只要知道其左上角的左上角方格所在行、列就可以唯一确定一个棋盘了,残缺方格或“伪”残缺方格直接用行、列号记录。

•tr棋盘中左上角方格所在行。

•tc棋盘中左上角方格所在列。

•dr残缺方块所在行。

•dl残缺方块所在列。

•size:

棋盘的行数或列数

3)数据结构设计:

用二维数组board[][],模拟棋盘。

覆盖残缺棋盘所需要的三格板(L牌)数目为:

(size2-1)/3;将这些三格板编号为1到(size2-1)/3。

则将残缺棋盘三格板编号的存储在数组board[][]的对应位置中,这样输出数组内容就是问题的解。

结合图4.4,理解算法。

四、算法实现

1、普通背包问题

1)声明变量、函数

//声明变量

intN;//物品数量

intM;//背包容量

doubleX[MAX];//背包问题最优解

doubleWeight[MAX];//物品重量

doublePrice[MAX];//物品价值

doubleunit_price[MAX];//物品单位价值

doubletotal_price;//背包总价值

//声明函数

voidInput();//输入函数

voidMergesort();//排序

voidknapsack();//背包装载

intOutput();//结果输出。

2)实现了按照价值密度的降序排列

voidMergesort()

{

doubletemp1,temp2,temp3;

for(inti=1;i

{

for(intj=1;j<=N-i;j++)

{

if(unit_price[j]

{

temp1=Price[j];

Price[j]=Price[j+1];

Price[j+1]=temp1;

temp2=Weight[j];

Weight[j]=Weight[j+1];

Weight[j+1]=temp2;

temp3=unit_price[j];

unit_price[j]=unit_price[j+1];

unit_price[j+1]=temp3;

}

}

}

}

3)求最大总价值

voidknapsack()

{

for(inti=1;i<=N;i++)//初始化X[i],所有物品没有放入背包

{

unit_price[i]=Price[i]/Weight[i];//计算物品单位价值unit_price[]

X[i]=0;

}

doubleleft=M;//背包剩余载重

Mergesort();//按单位价值将物品排序,便于贪心选择

while(left!

=0)

{

for(inti=1;i<=N;i++)//贪心选择,总是选择价值最大放入背包

{

if(Weight[i]<=left)//当前物品小于背包剩余载重

{//整个放入背包

X[i]=1;

left=left-Weight[i];

total_price=total_price+Price[i];

}

elseif(Weight[i]>left)

{//部分放入背包

X[i]=left/Weight[i];

left=0;

total_price=total_price+Price[i]*X[i];

}

}

}

}

2、0-1背包问题

1)声明变量、函数

#defineN100//最大物品数

//声明变量

doublec;//背包容量

intn;//物品数

doublew[N];//物品重量数组

doublep[N];//物品价值数组

doublecw;//当前重量

doublecp;//当前价值

doublebestp;//当前最优价值

intpath[N];//当前最优路径

intx[N];//最佳装载

//声明结构体Goods

structGoods

{

doubleweight;//物品重量

doubleprice;//总价值

intid;//物品编号

floatunit_price;//物品单位价值

};

Goodsgoods[N]={{0,0,0,0}};

//声明函数

voidbacktract(inti);//搜索解空间函数

doublebound(inti);//界限函数

voidInput();//输入函数

voidOutput();//输出函数

2)搜索解空间函数

//=============================搜索解空间====================

voidbacktract(inti)

{

//到达叶节点

if(i>=n)//i表示深度(层),i>n搜索到叶子节点

{

bestp=cp;

for(intj=0;j

x[j]=path[j];

return;

}

//搜索子树

if(cw+w[i]<=c)//当前物品放入背包不超载

{//进入左子树

cw+=w[i];

cp+=p[i];

path[i]=1;

backtract(i+1);//继续向下深度搜索

cw-=w[i];

cp-=p[i];

}

//进入右子树

if(bound(i+1)>bestp)//当前的节点不合适时,跳到下一个结点

{

path[i]=0;

backtract(i+1);

}

}

3)界限函数:

//======限界函数,计算当前价值与剩余价值和====================

doublebound(inti)

{

doublecleft=c-cw;//剩余容量

doublebound=cp;//当前物品价值

//以物品单位重量价值递减的顺序装入物品

while(i<=n&&w[i]<=cleft)//装载剩下的物品

{

cleft-=w[i];

bound+=p[i];

i++;

}//w[i]>cleft跳出循环,背包装满,物品部分装入背包

//装满背包

if(i<=n)

bound+=p[i]*cleft/w[i];

returnbound;//当前物品价值与剩余物品价值之和

}

3、棋盘覆盖问题

1)声明变量:

//声明变量

inttitle=1;//L型骨牌号

intboard[64][64];//二维数组board[][],模拟棋盘

/*

tr棋盘中左上角方格所在行。

tc棋盘中左上角方格所在列。

dr残缺方块所在行。

dl残缺方块所在列。

size:

棋盘的行数或列数

*/

2)棋盘覆盖分治实现:

voidchessBoard(inttr,inttc,intdr,intdc,intsize)

{

ints,t;

if(size==1)return;//size:

棋盘行数

t=title++;//L型骨牌号

s=size/2;//分割棋盘

//覆盖左上角子棋盘

if(dr

chessBoard(tr,tc,dr,dc,s);

else//此棋盘中无特殊方格

{

board[tr+s-1][tc+s-1]=t;//用t号L型骨牌覆盖右下角

chessBoard(tr,tc,tr+s-1,tc+s-1,s);//覆盖其余方格

}

//覆盖右上角子棋盘

if(dr=tc+s)//特殊方格在此棋盘中

chessBoard(tr,tc+s,dr,dc,s);

else//此棋盘中无特殊方格

{

board[tr+s-1][tc+s]=t;//用t号L型骨牌覆盖左下角

chessBoard(tr,tc+s,tr+s-1,tc+s,s);//覆盖其余方格

}

//覆盖左下角子棋盘

if(dr>=tr+s&&dc

chessBoard(tr+s,tc,dr,dc,s);

else//此棋盘中无特殊方格

{

board[tr+s][tc+s-1]=t;//用t号L型骨牌覆盖右上角

chessBoard(tr+s,tc,tr+s,tc+s-1,s);//覆盖其余方格

}

//覆盖右下角子棋盘

if(dr>=tr+s&&dc>=tc+s)//特殊方格在此棋盘中

chessBoard(tr+s,tc+s,dr,dc,s);

else//此棋盘中无特殊方格

{

board[tr+s][tc+s]=t;//用t号L型骨牌覆盖左上角

chessBoard(tr+s,tc+s,tr+s,tc+s,s);//覆盖其余方格

}

}

五、测试分析

1、普通背包问题

1)、输入物品个数N=3,如图6.1所示。

图6.1输入物品数

2)、输入背包容量M:

10,如图6.2所示。

图6.2输入背包容量

3)、输入各物品的大小或重量weight:

6、5、5,如图6.3所示。

图6.3输入物品重量

4)、输入各物品的价值p:

42、25、30,如图6.4所示。

图6.4输入物品价值

5)、显示背包装载结果,如图6.5所示。

图6.5结果显示

2、0-1背包问题

1)、输入背包容量:

10,如图6.6所示。

图6.6输入背包容量

2)、输入物品个数:

3,如图6.7所示。

图6.7输入物品数量

3)、输入各物品重量:

6、5、5,如图6.8所示。

图6.8输入物品重量

4)、输入各物品的价值:

42、25、30,如图6.9所示。

图6.9输入物品价值

5)、显示背包装载结果,如图6.10所示。

图6.10结果显示

3、棋盘覆盖问题

1)、输入size:

8,如图6.11所示。

图6.11输入棋盘大小

2)、输入特殊方块的位置:

43,如图6.12所示。

图6.12输入特殊方格位置

3)显示棋盘覆盖结果,如图6.13所示。

图6.13结果显示

六、结论

三个算法,普通背包问题是用的贪心算法设计的,0-1背包问题是用回溯算法实现的,棋盘覆盖问题是用的分治法设计的。

在开始设计时对贪心算法和分治算法的思想很好理解,但是在设计算法时大概思路还是有的,但写完算法写代码是发现写不出来,原因就是算法设计的还不够细,后来上网查了些资料,分析了别人的算法,最终实现了现在的算法设计。

在最终完成的程序中:

贪心算法求普通背包问题,基本上已经实现了主要的功能;01背包问题也能实现主要功能,但一开始未使用到界限函数,后来才加上;在分治算法求棋盘覆盖问题时,也基本上实现了它的功能,但在输入时还有不足,需要人工输入2的指数幂,不够方便。

而且界面不够友好,不能实现单步覆盖。

不过,总的来说这次实践也是有一定收获的,至少对书中这三个算法的知识进行了巩固,自己更进一步地理解了这三个算法——

贪心算法:

从问题的初始解出发逐步逼近给定的目标,每一步都做出当前看来是最优的选择(贪心选择),最终得到整个问题的最优解。

回溯法:

就是一种穷举搜索算法。

通俗地讲:

回溯法是一种“能进则进,进不了则换,换不了则退”的基本搜索方法。

在0-1背包问题中,为了提高搜索效率,避免一些无效的搜索,则需要用限界函数bound()剪去得不到最优解的子树。

分治法:

分而治之。

即将一个难以直接解决的大问题小规模化,待解出子问题解之后,将子问题解合并为该问题解。

七、源程序

1、普通背包问题

#include

#defineMAX100//物品件数最大值

//声明变量

intN;//物品数量

intM;//背包容量

doubleX[MAX];//背包问题最优解

doubleWeight[MAX];//物品重量

doublePrice[MAX];//物品价值

doubleunit_price[MAX];//物品单位价值

doubletotal_price;//背包总价值

//声明函数

voidInput();//输入函数

voidMergesort();//排序

voidknapsack();//背包装载

intOutput();//结果输出

//================输入函数====================================

voidInput()

{

inti;

cout<<"请输入物体数N:

";

cin>>N;

cout<

cout<<"请输入背包总容量M:

";

cin>>M;

cout<

cout<<"请输入各物体的大小或重量weight:

"<

for(i=1;i<=N;i++)

cin>>Weight[i];

cout<<"请输入各物体的价值price:

"<

for(i=1;i<=N;i++)

cin>>Price[i];

cout<

}

//================按照价值密度的降序排列===================

voidMergesort()

{

doubletemp1,temp2,temp3;

for(inti=1;i

{

for(intj=1;j<=N-i;j++)

{

if(unit_price[j]

{

temp1=Price[j];Price[j]=Price[j+1];Price[j+1]=temp1;

temp2=Weight[j];Weight[j]=Weight[j+1];Weight[j+1]=temp2;

temp3=unit_price[j];unit_price[j]=unit_price[j+1];unit_price[j+1]=temp3;

}

}

}

}

//===============实现背包装载================================

voidknapsack()

{

for(inti=1;i<=N;i++)//初始化X[i],所有物品没有放入背包

{

unit_price[i]=Price[i]/Weight[i];//计算物品单位价值unit_price[]

X[i]=0;

}

doubleleft=M;//背包剩余载重

Mergesort();//按单位价值将物品排序,便于贪心选择

while(left!

=0)

{

for(inti=1;i<=N;i++)//贪心选择,总是选择价值最大放入背包

{

if(Weight[i]<=left)//当前物品小于背包剩余载重

{//整个放入背包

X[i]=1;

left=left-Weight[i];

total_price=total_price+Price[i];

}

elseif(Weight[i]>left)

{//部分放入背包

X[i]=left/Weight[i];

left=0;

total_price=total_price+Price[i]*X[i];

}

}

}

}

//============结果输出======================================

intOutput()

{

charch;

cout<<"贪心算法实现背包装载结果为:

"<

for(inti=1;i<=N;i++)

{

cout<<"第"<

"<

"<

<<"物体价值密度:

"<

}

cout<

cout<<"向量表示:

"<<"(";

for(i=1;i<=N;i++)//输出背包问题最优解

{

cout<

}

cout<<")"<

cout<<"背包的最优装载值为:

"<

cout<<"按Y或y继续操作,否则按任意键"<

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2