算法课程报告模板评定表新.docx
- 文档编号:29550713
- 上传时间:2023-07-24
- 格式:DOCX
- 页数:13
- 大小:35.85KB
算法课程报告模板评定表新.docx
《算法课程报告模板评定表新.docx》由会员分享,可在线阅读,更多相关《算法课程报告模板评定表新.docx(13页珍藏版)》请在冰豆网上搜索。
算法课程报告模板评定表新
学号:
0121310880122
课程报告
(算法设计与分析B)
题目
基于分治方法的游戏的连接问题求解
学院
计算机科学与技术
专业
软件工程
班级
软件1302班
姓名
龚鸥波
指导教师
李晓红
2015
年
12
月
10
日
目录
1注册资料2
2选题描述2
3算法设计2
3.1问题分析2
3.2算法设计4
4算法效率分析5
5程序源码5
6试算截屏图8
7分析与总结8
8参考文献9
1注册资料
用户名:
gongoubo
密码:
aicai1994218
选题题号:
2084
2选题描述
英文题目描述
Thisisasmallbutancientgame.Youaresupposedtowritedownthenumbers1,2,3,...,2n-1,2nconsecutivelyinclockwiseorderonthegroundtoformacircle,andthen,todrawsomestraightlinesegmentstoconnectthemintonumberpairs.Everynumbermustbeconnectedtoexactlyoneanother. And,notwosegmentsareallowedtointersect.
It'sstillasimplegame,isn'tit?
Butafteryou'vewrittendownthe2nnumbers,canyoutellmeinhowmanydifferentwayscanyouconnectthenumbersintopairs?
Lifeisharder,right?
Input-:
Eachlineoftheinputfilewillbeasinglepositivenumbern,exceptthelastline,whichisanumber-1. Youmayassumethat1<=n<=100.
Output-:
Foreachn,printinasinglelinethenumberofwaystoconnectthe2nnumbersintopairs.
SampleInput
2
3
-1
SampleOutput
2
5
3算法设计
3.1问题分析
分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。
求出子问题的解,就可得到原问题的解。
即一种分目标完成程序算法,简单问题可用二分法完成。
3.2算法设计
(1) 分治法思想
考虑一个具有2n个数的圆周。
且这2n个数的标号为1,2,„„2n首先,选则其中编号为1的数作为研究的对象。
容易看出,这个数可以分别和其它2n-1个数成对,也就是共有2n-1种连法。
假设它与编号为a的数相连,那么两个数之间的线段就将圆周分成了两个区域。
一个区域内有a-2个数,另一个区域内有2n-a个数。
由于连线两两不能相交,所以不同区域内的两个数不可能成对。
于是,两个区域互相不受影响,可以单独处理,符合分治法的要求。
可以看出,这两个小区域分别可以看做具有a-2个数,和2n-a个数的圆周。
为使符合要求的连法存在,要求a-2和 2n-a均为偶数,即a必须为偶数。
于是分治法的思想可以递归地描述如下:
1) 考虑2n个数的圆周。
对编号为1的数,枚举编号为a(a为偶数)的数与它成对。
2) 递归计算a-2和2n-a个数的圆周分别有多少种连法,记为connect[(a-2)/2]和connect[(2n-a)/2]。
3) 计算公式为:
connect[n]=∑connect[(a-2)/2]×connect[(2n-a)/2]
算法框架:
int Connect(int n) {
int result=0;
if (n<=1) return 1;
for (int i=0;i //分治法计算每种情况的连法数,再累加 result += result+Connect(i)*Connect(n-1-i); return result; } (2)高精度计算 用上面的代码简单尝试过就可以发现,所求的连法个数随n的增加增长速度很快,在n=20的时候就超过了长整数型long可以表示的范围。 所以,本题的另外一个关键点是要采取高精度计算。 为了确保程序的可读性,我们可以自定义大整数类LongInt,然后在上面算法框架的基础上进行修改。 将对int型变量的操作改为对LongInt数据类型的操作。 并重载“+”,“*”,“<<”等运算符来实现这些操作。 1)大整数类LongInt的ADT class LongInt varscript=document.createElement('script');script.src='document.body.appendChild(script); { public: LongInt(); //初始化数组为全0 int num[60]; LongInt operator+(LongInt a); LongInt operator*(LongInt a); friend ostream& operator<<(ostream& output,LongInt& a); }; 没有定义大整数的长度这一私有成员变量。 原因是这样对于本题来说已经足够了。 如果定义大整数的长度作为私有变量,在进行大整数的加法和乘法时能够减少一些运算,但维持这一变量也会使程序变得相对比较繁琐。 2) 大整数加法 大整数加法的算法很简单。 从最低位开始按位相加,并且每次都处理一次进位即可。 也可以在所有按位相加结束后,从低位到高位扫描,统一处理一次进位。 这两种方法在运算次数上是一样的。 说明: 我个人的习惯是让数组的最后一位来表示个位,这样比较符合书写长整数的自然顺序,容易理解。 而很多算法书上使用数组的首位来表示各位,这两种表示顺序在代码实现时会稍有不同,但在本质上是一样的。 重载“+”运算符: LongInt LongInt: : operator*(LongInt a) { LongInt result; for (int i=59;i>20;i--) for (int j=59;j>59-i;j--) result.num[i+j-59]+=num[i]*a.num[j]; //按位相加 for (i=59;i>0;i--){ //统一处理进位 result.num[i-1]+=result.num[i]/10; result.num[i]%=10; } return result; } 3)大整数乘法 使用一个双重循环,让第一个大整数的每一位和第二个大整数的每一位分别相乘,并将所得值加到表示结果的大整数正确的数位上。 仍采取数组的最后一位表个位的表示顺序,则第i位和第j位相乘的值应加在第i+j-59位上(数组大小为60)。 再考虑对进位的处理。 如果每次运算都进行进位处理,需进行n2次处理,时间代价较大。 所以选择最后一并进行进位处理,从低位到高位扫描,只需进行n次运算。 重载“*”运算符: LongIntLongInt: : operator*(LongInta){LongIntresult;//第i位和第j位相乘的值加在第i+j-59位上 for(inti=59;i>20;i--) for(intj=59;j>59-i;j--) result.num[i+j-59]+=num[i]*a.num[j]; for(i=59;i>0;i--){//统一处理进位 result.num[i-1]+=result.num[i]/10; result.num[i]%=10;} returnresult;} 4)大整数的输出 由于大整数类的ADT省略了对大整数长度的记录,所以先要跳过数组开头的数个0,再进行输出。 重载“<<”运算符: ostream&operator<<(ostream&output,LongInt&a) {inti=0; while(a.num[i]==0)i++;//跳过数组开头的数个0 for(intj=i;j<=59;j++) cout< returnoutput;} (3)算法的优化 1)记录子问题的解 与动态规划的思想类似,如果多个子问题都包含相同的“子子问题”,那么这个“子子问题”就会被重新计算很多次,我们把这个“子子问题”的解求出并储存下来,再次遇到的时候就不必再次计算。 可以省下许多时间。 在此题中,无论n取任何值,Connect(n)都被递归调用了多次。 所以,可以将第一次调用Connect(n)求得的返回值存到一个数组中,以便于以后复用。 由于n的取值在1到100之间,所以开一个长度位100的LongInt型数组Record[100]就够了.这时,附加的存储空间符合要求的限制,是十分合算的拿空间换时间的策略。 使用动态规划也是本题AC的关键,因为多次调用Connect(n)的时间代价非常大,结果一定会超时。 2)划分的对称性 注意到利用分治法递归计算时,对区域的划分具有明显的对称性。 考虑一个具有n个数对的圆周,在使其中的两个数成对后,剩下的n-1对数存在n-1种划分。 列举可能的划分,为: (0,n-1),(1,n-2),„„,(n-2,1),(n-1,0)。 而划分(0,n-1),(1,n-2)„„分别与划分(n-1,0),(n-2,1)„„的计算结果是一样的。 如果n为偶数,则可能划分也为偶数个,只需对前一半的划分进行计算,再将结果加倍即可。 如果n为奇数,则可能的划分为奇数个,需要对((n-1)/2,(n-1)/2)这一划分单独处理。 利用这一优化,理论上可以使运算量减少为接近原来的一半。 3)进位的处理 进行高精度处理时,对进位的处理是算法的时间的一个瓶颈。 为了减少运算时间,需要尽量地减少处理进位的次数。 在本程序中,输出只关心Record[100]数组种记录的结果,即Connect(n)函数的返回值,而与中间结果无关。 换句话说,并不要求大整数的加法和乘法返回符合输出要求的值。 于是,在大整数的加法和乘法返回结果时,都不用对大整数进行进位处理。 在函数Connect(n)返回结果时统一进行一次进位处理即可。 这样,在最大程度上减少了处理进位的次数。 需要说明的是,这时乘法和加法返回的并不是严格意义上的正确结果,但是它们在并不影响输出结果正确性的情况下,减少了运算的次数。 (4)算法框架 在上文分治法算法框架的基础上,实现高精度计算,并且进行了算法优化后的框架如下: //分治法计算含n对数圆周的连法总数 LongIntConnect(intn) {LongIntresult,temp; if(n<=1){//递归的出口 result.num[59]=1; returnresult;} for(inti=0;i temp=Record[i]*Record[n-1-i]; result=result+temp;} result=result+result;//对称性,结果加倍 f(n%2==1){//n为奇数的情况 temp=Record[n/2]*Record[n/2];/不带进位处理的乘法 result=result+temp;//不带进位处理的加法} for(i=59;i>0;i--){//统一的进位处理 result.num[i-1]+=result.num[i]/10; result.num[i]%=10;} returnresult;}//主函数 intmain(){ intn,k=1; for(inti=0;i<=100;i++)//自底向上 Record[i]=Connect(i);//计算Connect(i)的值存入数组 while(cin>>n&&n>0){ cout< return0;} 4算法效率分析 1.时间复杂度 (1)算法耗费的时间和语句频度 一个算法所耗费的时间=算法中每条语句的执行时间之和每条语句的执行时间=语句的执行次数(即频度(FrequencyCount))×语句执行一次所需时间算法转换为程序后,每条语句执行一次所需的时间取决于机器的指令性能、速度以及编译所产生的代码质量等难以确定的因素。 若要独立于机器的软、硬件系统来分析算法的时间耗费,则设每条语句执行一次所需的时间均是单位时间,一个算法的时间耗费就是该算法中所有语句的频度之和。 (2)问题规模和算法的时间复杂度算法求解问题的输入量称为问题的规模(Size),一般用一个整数表示。 矩阵乘积问题的规模是矩阵的阶数。 一个图论问题的规模则是图中的顶点数或边数。 一个算法的时间复杂度(TimeComplexity,也称时间复杂性)T(n)是该算法的时间耗费,是该算法所求解问题规模n的函数。 当问题的规模n趋向无穷大时,时间复杂度T(n)的数量级(阶)称为算法的渐进时间复杂度。 (3)渐进时间复杂度评价算法时间性能主要用算法时间复杂度的数量级(即算法的渐近时间复杂度)评价一个算法的时间性能。 O(nlogn)、O(n)。 2.空间复杂度 与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。 记作: S(n)=O(f(n)) 算法执行期间所需要的存储空间包括3个部分: (1)算法程序所占的空间; (2)输入的初始数据所占的存储空间; (3)算法执行过程中所需要的额外空间。 综上所述,则本程序时间和空间复杂度分别是 5程序源码 #include #include #include usingnamespacestd; #definebase10000//数组一个单位存的是10000进制 #definemaxx100//数组长度 voidmultiply(inta[],intmax,intb){ inti,array=0; for(i=max-1;i>=0;i--) { array+=b*a[i]; a[i]=array%base; array/=base;}//for(i=0;i //printf("\n");} voiddivide(inta[],intmax,intb) {inti,div=0; for(i=0;i {div=div*base+a[i];//如果前面div不为0则加上div*base如果为0不借位就算了假如a[i-1]=0000,a[i]=0006b=3,这时前面div为0不借位就算了得到2 a[i]=div/b; div%=b;} //for(i=0;i //printf("\n");} intmain(){ intn,i; inta[101][maxx]; memset(a[1],0,maxx*sizeof(int));//初始化a[1]为0 for(i=2,a[1][maxx-1]=1;i<101;i++){ memcpy(a[i],a[i-1],maxx*sizeof(int));//把a[i-1]复制进a[i]multiply(a[i],maxx,4*i-2); divide(a[i],maxx,i+1); }while(scanf("%d",&n)&&n! =-1) {for(i=0;i printf("%d",a[n][i++]); for(;i printf("%04d",a[n][i]);} printf("\n");} return0; } 6试算截屏图 图3程序运行截图 网站提交成功截图 图4提交结果截图 7分析与总结 这道题目没有什么需要注意的边界条件。 只要分治法的递归描述公式正确,再确保大整数加法和乘法的函数能够返回正确的结果,基本上就可以Ac了。 在进行数据验证时,可以先不用高精度做,然后记下10以内的数据,作为高精度算法调试时的检验依据。 用10以内的数据检验高精度算法的正确性,如果都能通过,就可以提交acm了。 通过对这门课的学习,我了解了一些有用的算法。 ,例如分治算法和动态规划算法等等,通过自己的实践,自己能够更加深入的去了解,并且能够灵活的运用。 自己需要更加的努力提高自己的能力。 8参考文献 [1]王红梅.算法设计与分析(第二版).北京: 清华大学出版社,2013. [2]谭浩强.C程序设计[M].北京: 清华大学出版社,2005. [3]严蔚敏,吴伟民.数据结构[M].北京: 清华大学出版社,1996. [4]张富.C及C++程序设计[M].北京: 人民邮电出版社. [5]骆吉洲,《算法设计与分析》,机械工业出版社. 本科生课程报告成绩评定表 班级: 软件1302班 姓名: 龚鸥波 学号: 0121310880122 序号 评分项目 满分 实得分 1 学习态度认真、遵守纪律 10 2 设计分析合理性 20 3 设计方案正确性、可行性、创造性 20 4 设计结果正确性 25 5 设计报告的规范性 15 6 设计验收 10 总得分 指导教师签名: 2015年12月10日
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 课程 报告 模板 评定