《算法分析与设计》10个期末拿97的实验报告.docx
- 文档编号:29860243
- 上传时间:2023-07-27
- 格式:DOCX
- 页数:38
- 大小:568.86KB
《算法分析与设计》10个期末拿97的实验报告.docx
《《算法分析与设计》10个期末拿97的实验报告.docx》由会员分享,可在线阅读,更多相关《《算法分析与设计》10个期末拿97的实验报告.docx(38页珍藏版)》请在冰豆网上搜索。
《算法分析与设计》10个期末拿97的实验报告
算法分析与设计系列实验报告
前言
首先我想说《算法分析与设计》这门课真的很难,虽然平时都有认真听讲,但也有不少书上的算法还是似懂非懂。
于是我想,其他的算法暂且不强求全部弄懂,但至少本学期实验中的这十个算法必须要完全学透。
正是抱着这样的心态,我整理、编写了这份实验报告。
它可以在一定程度上反映我学习《算法分析与设计》这门课的方式方法,对于我个人来讲是一份值得珍藏的文档。
此报告中每个单项实验主要包括实验描述、实验思路与分析和实验心得三部分。
其中实验思路与分析可以说是按照真实实验过程中我个人的思路变化(理解问题、思考问题、参考资料、解决问题)来编写的,所以可能显得比较啰嗦,但贵在真实,还请老师谅解。
此外,为了使此报告不显得过于冗长,我使用了“插入附件”的功能,将TXT格式的源代码插入到文中,直接双击即可查看。
如果您的word不支持此项功能,也可直接在文件夹中查看TXT文本。
而且在“源代码”文件夹在还有*.CPP格式的、10个实验的全部12个源代码。
所有源代码都在VC6.0上运行调试过,可直接打开运行。
正文
实验一C/C++环境及递归算法
一、实验描述
Ø实验目的与要求
1、熟悉C/C++语言的集成开发环境;
2、通过本实验加深对递归过程的理解
Ø实验内容:
掌握递归算法的概念和基本思想,分析并掌握“整数划分”问题的递归算法。
Ø实验题
任意输入一个整数,输出结果能够用递归方法实现整数的划分。
Ø实验步骤
1.理解算法思想和问题要求;
2.编程实现题目要求;
3.上机输入和调试自己所编的程序;
4.验证分析实验结果;
5.整理出实验报告。
二、实验思路及步骤
(1)输入一个正整数,输出它有多少种不同的划分:
要完成这个任务相当简单,因为最重要的代码在书上已经给出,我们只需要加入输入和输出就可以成为一个可运行的代码
(2)源代码:
(3)运行结果:
(4)创新亮点:
但我不满足只是实现这么点功能,我于是开始思考能不能
输入一个整数,输出它所有的划分以及划分的种类
例如输入6输出以下划分:
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1。
1》算法思路(以6举例说明):
首先,我们可以采用集合的思维去考虑,比如对于整数6,则初始集合相当于{1,1,1,1,1,1}。
从1+1+1+1+1+1到2+1+1+1+1实际上就相当于我{1,1,1,1,1,1}的集合中拿两个1出来相加然后再把结果放回集合当中得到{2,1,1,1,1}.若这个时候我继续拿集合里面的两个1相加再放回去就可以得到{2,2,1,1},同理再做同样的处理的话我们会得到{2,2,2}。
我们用2个栈来模拟这样的操作,栈s1(栈顶top1)用来“存放”‘1’,栈s2(栈顶top2)用来存放要输出的划分,如(42,3111)。
准确来说,s1中具体存放着什么不重要,因为top1为6则表示有6个‘1’,我们只用修改top1,而不用管其中的具体值。
而s2中存着的是具体数。
又因为top2<=top1,所以只用开辟top1个储存单元即可,top1、top2都在其上移动。
但在概念理解上还是2个栈。
我们用递归来做,而递归要有“边界条件”+“递归方程”。
我们来设定这样的“边界条件”:
先输出划分中最大整数为6的——6;
再是5——5+1;
再是4——4+2,4+1+1;
……
最后是1——1+1+1+1+1+1;
所以此递归的边界是先输出划分中最大整数为1(边界A)但在先输出划分中最大整数确定的前提下还有多种不同的输出,它也可看做一种递归,它的边界就是我们前面提到的“s1栈为空(‘1’被取完)为边界条件(边界B)要在代码上实现边界A很简单:
for(inti=top1;i>=1;i--){
……}
实现边界B也不难:
if(top1==0)
display();//输出此次划分
所以难点就在于“递归方程”了,也就是我们前面说的取‘1’的问题:
for(inti=top1;i>=1;i--){
if(top2==0||i<=a[top2-1]){//确保你此次取得‘1’的个数不超过上次,才能由大到小输出,不重复不遗漏
a[top2]=i;//先将此次划分的最大整数压入s2
partion(top1-i,top2+1,a);//接着取剩下的‘1’,相当于对剩下的数再进行一次整数的划分,这个思想相当重要!
这也是本题的“递归方程”
}
2》完整源代码:
3》运行结果:
三、实验心得
(1)以前只会使用最简单的算术类型的递归,在学习了第一章并完成了实验之后,开始慢慢理解递归的精髓。
在分析递归算法时懂得了从“边界条件”和“递归方程”方面分析,对递归算法的理解更透彻了。
(2)学会使用malloc/free函数
四、【附】malloc函数使用方法
Malloc向系统申请分配指定size个字节的内存空间,分配成功返回类型是void*;分配失败返回NULL。
所以如果要分配的内存比较大的话,一定要进行返回值的判断。
C,C++规定,void*类型可以强制转换为任何其它类型的指针。
所以malloc前的强制类型转换是必须的。
而且用malloc分配的内存不再使用时,应使用free()函数将内存块释放。
Malloc的头文件在VisualC++6.0中可以用malloc.h或者stdlib.h,在TC2.0中可以用malloc.h或alloc.h。
malloc和new的区别是:
1,malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。
它们都可用于申请动态内存和释放内存。
2,对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。
对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
3,因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete。
注意new/delete不是库函数。
4,C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
5、new可以认为是malloc加构造函数的执行。
new出来的指针是直接带类型信息的。
而malloc返回的都是void指针。
实验二棋盘覆盖问题
一、实验描述
Ø实验目的与要求
1、掌握棋盘覆盖问题的算法;
2、初步掌握分治算法
Ø实验题:
棋盘覆盖问题:
在一个2k×2k个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。
在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
Ø实验提示
voidchessBoard(inttr,inttc,intdr,intdc,intsize)
{
if(size==1)return;
intt=tile++, //L型骨牌号
s=size/2; //分割棋盘
//覆盖左上角子棋盘
if(dr
//特殊方格在此棋盘中
chessBoard(tr,tc,dr,dc,s);
else{//此棋盘中无特殊方格
//用t号L型骨牌覆盖右下角
board[tr+s-1][tc+s-1]=t;
//覆盖其余方格
chessBoard(tr,tc,tr+s-1,tc+s-1,s);}
//覆盖右上角子棋盘
if(dr
//特殊方格在此棋盘中
chessBoard(tr,tc+s,dr,dc,s);
else{//此棋盘中无特殊方格
//用t号L型骨牌覆盖左下角
board[tr+s-1][tc+s]=t;
//覆盖其余方格
chessBoard(tr,tc+s,tr+s-1,tc+s,s);}
//覆盖左下角子棋盘
if(dr>=tr+s&&dc //特殊方格在此棋盘中 chessBoard(tr+s,tc,dr,dc,s); else{//用t号L型骨牌覆盖右上角 board[tr+s][tc+s-1]=t; //覆盖其余方格 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{//用t号L型骨牌覆盖左上角 board[tr+s][tc+s]=t; //覆盖其余方格 chessBoard(tr+s,tc+s,tr+s,tc+s,s);} } 二、实验思路及步骤 (1)“棋盘覆盖问题”其实在书上已经解释得非常详细了,甚至是代码书上也基本上给了出来。 在这里只给出核心思想: k>0时,可将2k×2k的棋盘划分为4个2k-1×2k-1的子棋盘,如图4.11(a)所示。 这样划分后,由于原棋盘只有一个特殊方格,所以,这4个子棋盘中只有一个子棋盘包含该特殊方格,其余3个子棋盘中没有特殊方格。 为了将这3个没有特殊方格的子棋盘转化为特殊棋盘,以便采用递归方法求解,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如图4.11(b)所示,从而将原问题转化为4个较小规模的棋盘覆盖问题。 递归地使用这种划分策略,直至将棋盘分割为1×1的子棋盘。 (2)将“实验提示”的代码补全,使之成为一段有输入、输出的可执行代码: (3)运行结果: (4)创新亮点: 此棋盘覆盖问题的算法要可行,必须有一个前提,那就是棋盘的边界大小(size)必须是2的正整数次幂。 很多同学没有意思到这一点,在将代码补齐后,输入6、12、14等数据也是有输出的,但输出明显不正确,还以为是代码哪里打错了。 所以我就想到了加一个验证程序,保证输入正确—— 增加了一个power()方法用于判断输入的size是否是2的正整数次幂 考虑到结果是输出到控制台,棋盘的size超过64就显得没有多少价值,所以只是构造了一个数组intf[]={2,4,8,16,32,64,128,256};将输入的size与之逐个比较即可,Board数组也只定义了256*256。 实际上还可以用递归的方法判断输入的size是否为2的正整数次幂: boolpower(intn) { if(n==2) return1; if(n%2||n==0) return0; else returnpower(n/2); } 三、实验心得 要注意: 由于数组的下标是从0开始的,所以特殊方格的坐标不是[br][bc],而是[br-1][br-1]。 如果没有注意到这一点,输出的结果会出错。 实验三分治算法 一、实验描述 Ø实验目的与要求 1、熟悉二分搜索算法; 2、初步掌握分治算法; Ø实验题 【1】设a[0: n-1]是一个已排好序的数组。 请改写二分搜索算法,使得当搜索元素x不在数组中时,返回小于x的最大元素的位置I和大于x的最大元素位置j。 当搜索元素在数组中时,I和j相同,均为x在数组中的位置。 【2】设有n个不同的整数排好序后存放于t[0: n-1]中,若存在一个下标I,0≤i<n,使得t[i]=i,设计一个有效的算法找到这个下标。 要求算法在最坏的情况下的计算时间为O(logn)。 Ø实验提示 1、用I,j做参数,且采用传递引用或指针的形式带回值。 boolBinarySearch(inta[],intn,intx,int&i,int&j) { intleft=0; intright=n-1; while(left { intmid=(left+right)/2; if(x==a[mid]) { i=j=mid; returntrue; } if(x>a[mid]) left=mid+1; else right=mid-1; } i=right; j=left; returnfalse; } intSearchTag(inta[],intn,intx) { intleft=0; intright=n-1; while(left { intmid=(left+right)/2; if(x==a[mid])returnmid; if(x>a[mid]) right=mid-1; else left=mid+1; } return-1; } 二、实验思路及步骤 (1)这两道题都是在“二分查找”的基础上加一点点操作—— 分析题【1】: 当用“二分查找”查找的数在数组中时只用在if(x==a[mid])条件语句下加个输出语句而已;当查找的数比数组中最小的还小时,mid必定小于0;当查找的数不数组中最大的数还大时,mid必定大于n-1;当查找的数既没有超过上限,也没有超过下限时,mid必定等于小于x的最大元素的位置I或大于x的最大元素位置j,这时只要判断a[mid]和x的大小即可确定是哪种情况。 分析题【2】: “排好序后”、“要求算法在最坏的情况下的计算时间为O(logn)”就是要求我们用“二分查找”。 在排好序后,如果a[mid]>mid,接下来就在它的左边找;如果a[mid] (这算法非常重要! ! ! ! ! ) (2)题【1】的代码: (3)题【1】代码的结果: (4)题【2】的代码: (5)题【2】代码运行结果: (6)创新亮点 输入数组时不需要事先输入数组的大小,不需要按照顺序输入,程序会自动计算你输入的数组的大小和给数组排序(利用 这在上述2段代码中均有体现 三、实验心得 题【2】中的算法的必要条件是“不同整数”,一开始没有认识到,输入数据时有重复数据,导致结果不理想,还以为代码有问题,浪费了很多时间。 由此我知道以后一定要认真审题,不要漏掉每一个细节。 实验四动态规划算法1 一、实验描述 Ø实验目的与要求 1、熟悉最长公共子序列问题的算法; 2、初步掌握动态规划算法; Ø实验题 若给定序列X={x1,x2,…,xm},则另一序列Z={z1,z2,…,zk},是X的子序列是指存在一个严格递增下标序列{i1,i2,…,ik}使得对于所有j=1,2,…,k有: zj=xij。 例如,序列Z={B,C,D,B}是序列X={A,B,C,B,D,A,B}的子序列,相应的递增下标序列为{2,3,5,7}。 给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。 【1】给定2个序列X={x1,x2,…,xm}和Y={y1,y2,…,yn},找出X和Y的最长公共子序列。 【2】改进LCS函数,不使用数组b而仅借助数组c本身在O(m+n)时间内构造最长公共子序列。 Ø实验提示 include"stdlib.h" #include"string.h" voidLCSLength(char*x,char*y,intm,intn,int**c,int**b) { inti,j; for(i=1;i<=m;i++)c[i][0]=0; for(i=1;i<=n;i++)c[0][i]=0; for(i=1;i<=m;i++) for(j=1;j<=n;j++) { if(x[i]==y[j]) { c[i][j]=c[i-1][j-1]+1; b[i][j]=1; } elseif(c[i-1][j]>=c[i][j-1]) { c[i][j]=c[i-1][j]; b[i][j]=2; } else { c[i][j]=c[i][j-1]; b[i][j]=3; } } } voidLCS(inti,intj,char*x,int**b) { if(i==0||j==0)return; if(b[i][j]==1) { LCS(i-1,j-1,x,b); printf("%c",x[i]); } elseif(b[i][j]==2) LCS(i-1,j,x,b); elseLCS(i,j-1,x,b); } 二、实验思路及步骤 (1)利用动态规划先计算最长公共子序列的长度,即调用voidLCSLength(char*x,char*y,intm,intn,int**c,int**b);再根据长度,然后通过回溯求出最长公共子序列,即调用voidLCS(inti,intj,char*x,int**b)。 (2)完整代码 (3)运行结果 实验五动态规划算法2 一、实验描述 Ø实验目的与要求 1、熟悉最长最大子段和问题的算法; 2、进一步掌握动态规划算法; Ø实验题 若给定n个整数组成的序列a1,a2,a3,……an,求该序列形如ai+ai+1+……+an的最大值。 二、实验提示 intMaxSum(intn,int*a,int&besti,int&bestj) { intsum=0; for(inti=1;i<=n;i++) for(intj=i;j<=n;j++) { intthissum=0; for(intK=i;k<=j;k++)thissum+=a[k]; if(thissum>sum) { sum=thissum; besti=i; bestj=j; } } returnsum; } intMaxSum(intn,int*a,int&besti,int&bestj) { intsum=0; for(inti=1;i<=n;i++) { intthissum=0; for(intj=i;j<=n;j++) { thissum+=a[j]; if(thissum>sum) { sum=thissum; besti=i; bestj=j; } } } returnsum; } 三、实验思路及步骤 (1)研究实验提示中的代码,发现这实际上是穷举法的代码,而不是动态规划的代码 (2)下面我们按动态规划的思路分析: ●分析最优解的结构 记b[j]为所有字段和a[i]~a[j](1<=i<=j)中的最大值,则{a[1],a[2].a[3],……,a[n]}的最大字段和等于maxb[j](1<=j<=n)。 由b[j]的定义可知,当b[j-1]>0时b[j]=b[j-1]+a[j];当b[j-1]<0时b[j]=a[j] ●建立递归关系 由上可知: b[j]=max{b[j-1]+a[j],a[j]},1<=j<=n ●计算最优值 利用递归关系自底向上计算最优值,请看核心代码: intMaxSum(intn,int*a){ intsum=0,b=0; for(inti=1;i<=n;i++){ if(b>0)b+=a[i]; elseb=a[i]; if(b>sum)sum=b; } returnsum; } ●构造最优解 我的思路是先找到maxb[j],从而可以确定它的下标;然后根据在满足最大子段和的区间内必有b[i]-a[i]=0找到满足最大子段和的最小下标。 但这样MaxSum函数是需要需要修改的: intMaxSum(intn,int*a){//求最大子段和的值并构造数组b[n] intsum=0; b[0]=0; for(intj=1;j<=n;j++){ b[j]=(b[j-1]+a[j]>a[j])? b[j-1]+a[j]: a[j]; if(sum } returnsum; } (3)程序完整代码: 如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。 copyright@ 2008-2022 冰点文档网站版权所有 经营许可证编号:鄂ICP备2022015515号-1