算法导论报告.docx
- 文档编号:4023573
- 上传时间:2022-11-27
- 格式:DOCX
- 页数:16
- 大小:42.51KB
算法导论报告.docx
《算法导论报告.docx》由会员分享,可在线阅读,更多相关《算法导论报告.docx(16页珍藏版)》请在冰豆网上搜索。
算法导论报告
算法导论软件4班魏仁斌20122712
问题一:
同时求n个元中的最大元与次大元
思路:
分冶法
1.算法设计:
if(问题不可分)
{直接求解;
返回问题的解;
}
else{
对原问题进行分治;
递归对每一个分治的部分求解;
归并整个问题,得出全问题的解;
}
2.算法描述:
分治就是“分而治之”的意思,其实质就是将原问题分成n个规模较小而结构与原问题相似的子问题;然后递归地解这些子问题,最后合并其结果就得到原问题的解。
当n=2时的分片法又称二分法。
分治步骤:
(1)分解(Divide):
将原问题分成一系列子问题。
(2)解决(Conquer):
递归地解各子问题。
若子问题足够小,则可直接求解。
(3)合并(combine);将子问题的结果合并成原问题的解。
复杂度:
通常算法,设置一个变量,等于需要比较的数组的第一个元素,然后依次与后面的n-1进行比较,需要比较n-1次得到最大元。
源程序代码:
#include"iostream.h"
#defineN10
intmax(inta,intb)
{return((a>b)?
a:
b);}
intmin(inta,intb)
{return((a
a:
b);}
voidSearch(inta[],int*max0,int*second0,intn)
{intg[30];
inti,m;
intmax1,max2,second1,second2;
if(n==1)
{*max0=a[0];*second0=a[0];}
elseif(n==2)
{*max0=max(a[0],a[1]);
*second0=min(a[0],a[1]);
}
else
{m=n/2;
for(i=0;i g[i]=a[i]; Search(g,&max1,&second1,m); for(i=0;i g[i]=a[i+m]; Search(g,&max2,&second2,n-m); *max0=max(max1,max2); *second0=max(min(max1,max2),max(second1,second2)); } } voidmain() { cout<<"用分治法同时求最大元和次大元\n"; inta[N]; inti,max,second; cout<<"输入"< \n"; for(i=0;i cin>>a[i]; Search(a,&max,&second,N); cout<<"输出结果: \n"; cout<<"max="< } 测试结果: 分析: 利用递归速度较慢,但是可以把问题简单化。 问题二: 大整数的乘法 思路: 分冶递归思想。 设X和Y是两个n位的整数,假定n是2的整数次幂。 把每个整数分为两部分,每部分为n/2位,则X和Y可重写为X=x1*10n/2+x0和Y=y1*10n/2+y0,X和Y的乘积可以计算为: X*Y=(x1*10n/2+x0)*(y1*10n/2+y0) =X*Y*10n+((x1+x0)*(y1+y0)-x1*y1-x0*y0)*10n/2+x0*y0 由此体现了分治递归的思想,将大整数化小,规模也变小。 复杂度: T(n)=O(nlog3)=O(n1.59) 源代码: #include #include #include #defineN100//最大100位 /*函数声明*/ voidcalc1(char*str1,intlen1,int*tmp,intm); voidaccumulate(intcnt,int*res,intres_len,int*tmp,inttmp_len); char*bignum_multi(char*str1,intlen1,char*str2,intlen2,char*result,intlen); intmain() { inti,j; /*获取计算数据(可以从文件中读取)*/ charstr1[N]={NULL}; charstr2[N]={NULL}; charresult[N*N]={NULL}; printf("InputNum1: \n"); scanf("%s",str1); fflush(stdin); printf("InputNum2: \n"); scanf("%s",str2); /*计算两个字符串的长度,及存储结果所需要的空间*/ intlen1=strlen(str1),len2=strlen(str2); intlen=len1+len2; /*计算并输出计算结果*/ printf("Theresultis: %s\n",bignum_multi(str1,len1,str2,len2,result,len)); return0; } /*=============================================================== 调用calc1和accumulate函数计算大数相乘 ===============================================================*/ char*bignum_multi(char*str1,intlen1,char*str2,intlen2,char*result,intlen) { inti,j,m=0,cnt=0,*tmp,*res; /*分配临时结果的存放空间*/ tmp=(int*)malloc((len1+1)*sizeof(int)); res=(int*)malloc(len*sizeof(int)); /*初始化两个数组*/ for(i=0;i tmp[i]=0; for(j=0;j res[j]=0; for(i=len2-1;i>=0;i--) { /*获取乘数中第i位的值*/ m=str2[i]-'0'; /*计算被乘数与第i位的乘积,结果保存在tmp整型数组中*/ calc1(str1,len1,tmp,m); /*将tmp数组中的值加到res数组中*/ cnt++; accumulate(cnt,res,len,tmp,len1+1); } /*将整形数组res中的值转化成字符串存入result中*/ i=0;j=0; /*去掉res中第一个非零数字前的零*/ while(res[i++]==0); for(m=i-1;m result[j]=res[m]+0x30; result[j]='\0'; free(tmp); free(res); returnresult; } /*=============================================================== 计算被乘数与乘数的某一位的乘积 ===============================================================*/ voidcalc1(char*str1,intlen1,int*tmp,intm) { /*d两个位的乘积结果,remainder余数,carry进位*/ inti,d=0,remainder=0,carry=0; /*从被乘数字符串'\0'的前一位算起*/ for(i=len1-1;i>=0;i--) { d=str1[i]-'0'; d*=m; remainder=(d+carry)%10; carry=(d+carry)/10; tmp[i+1]=remainder; } if(carry) tmp[0]=carry; else tmp[0]=0; } /*=============================================================== 将被乘数与乘数中一位数字的乘积结果计入res数组中 ===============================================================*/ voidaccumulate(intcnt,int*res,intlen,int*tmp,intlen1) { intm=0,n=0,i,k,remainder=0; staticintcarry=0; for(k=len1-1,i=0;k>=0;k--,i++) { m=tmp[k]; n=res[len-cnt-i]; if(m+n+carry>=10) { remainder=(m+n+carry)%10; carry=1; } else { remainder=m+n+carry; carry=0; } res[len-cnt-i]=remainder; } } 测试结果: 分析: 利用分治法可以将问题简化,有助于解决一些复杂性较大的问题,提高程序的运行效率。 问题三: 最长公共子序列(LCS) 思路: 采用动态规划法求解 1、序列str1和序列str2 ·长度分别为m和n; ·创建1个二维数组L[m.n]; ·初始化L数组内容为0 ·m和n分别从0开始,m++,n++循环: -如果str1[m]==str2[n],则L[m,n]=L[m-1,n-1]+1; -如果str1[m]! =str2[n],则L[m,n]=max{L[m,n-1],L[m-1,n]} ·最后从L[m,n]中的数字一定是最大的,且这个数字就是最长公共子序列的长度 ·从数组L中找出一个最长的公共子序列 2、从数组L中查找一个最长的公共子序列 i和j分别从m,n开始,递减循环直到i=0,j=0。 其中,m和n分别为两个串的长度。 ·如果str1[i]==str2[j],则将str[i]字符插入到子序列内,i--,j--; ·如果str1[i]! =str[j],则比较L[i,j-1]与L[i-1,j],L[i,j-1]大,则j--,否则i--;(如果相等,则任选一个) 复杂度: 在LCS算法中,每一次递归调用使i或j减1,因此算法的时间复杂度为O(m+n)。 源代码: #include #include usingnamespacestd; intmain(intargc,char**argv) { stringstr1="ABCBDAB"; stringstr2="BDCABA"; intx_len=str1.length(); inty_len=str2.length(); intarr[50][50]={{0,0}}; inti=0; intj=0; for(i=1;i<=x_len;i++) { for(j=1;j<=y_len;j++) { if(str1[i-1]==str2[j-1]) { arr[i][j]=arr[i-1][j-1]+1; } else { if(arr[i][j-1]>=arr[i-1][j]) { arr[i][j]=arr[i][j-1]; } else { arr[i][j]=arr[i-1][j]; } } } } for(i=0;i<=x_len;i++) { for(j=0;j<=y_len;j++) { cout< } cout< } for(i=x_len,j=y_len;i>=1&&j>=1;) { if(str1[i-1]==str2[j-1]) { cout< i--; j--; } else { //if(arr[i][j-1]>=arr[i-1][j])//打印: BADB if(arr[i][j-1]>arr[i-1][j])//打印: ABCB { j--; } else { i--; } } } cout< return0; } 测试结果: 分析: 解决这个LCS问题,你要求三个方面的东西: 1>LCS(Xm-1,Yn-1)+1; 2>LCS(Xm-1,Y),LCS(X,Yn-1); 3>max{LCS(Xm-1,Y),LCS(X,Yn-1)}; 问题四: 矩阵相乘次序 思路: 采用动态规划法求解 设计算A[i: j],1≤i≤j≤n,所需要的最少数乘次数m[i,j],则原问题的最优值为m[1,n]。 当i=j时,A[i: j]=Ai,因此,m[i][i]=0,i=1,2,…,n 当i j]的最优次序在Ak和Ak+1之间断开,i<=k m[i][j]=m[i][k]+m[k+1][j]+pi-1pkpj。 由于在计算是并不知道断开点k的位置,所以k还未定。 不过k的位置只有j-i个可能。 因此,k是这j-i个位置使计算量达到最小的那个位置。 综上,有递推关系如下: 构造最优解: 若将对应m[i][j]的断开位置k记为s[i][j],在计算出最优值m[i][j]后,可递归地由s[i][j]构造出相应的最优解。 s[i][j]中的数表明,计算矩阵链A[i: j]的最佳方式应在矩阵Ak和Ak+1之间断开,即最优的加括号方式应为(A[i: k])(A[k+1: j)。 因此,从s[1][n]记录的信息可知计算A[1: n]的最优加括号方式为(A[1: s[1][n]])(A[s[1][n]+1: n]),进一步递推,A[1: s[1][n]]的最优加括号方式为(A[1: s[1][s[1][n]]])(A[s[1][s[1][n]]+1: s[1][s[1][n]]])。 同理可以确定A[s[1][n]+1: n]的最优加括号方式在s[s[1][n]+1][n]处断开...照此递推下去,最终可以确定A[1: n]的最优完全加括号方式,及构造出问题的一个最优解。 复杂度: n^3 源代码: //#include"stdafx.h" #include usingnamespacestd; constintL=7; intMatrixChain(intn,int**m,int**s,int*p); voidTraceback(inti,intj,int**s);//构造最优解 intmain() { intp[L]={15,5,20,10,30,25,35}; int**s=newint*[L]; int**m=newint*[L]; for(inti=0;i { s[i]=newint[L]; m[i]=newint[L]; } cout<<"矩阵的最少计算次数为: "< cout<<"矩阵最优计算次序为: "< Traceback(1,6,s); return0; } intMatrixChain(intn,int**m,int**s,int*p) { for(inti=1;i<=n;i++) { m[i][i]=0; } for(intr=2;r<=n;r++)//r为当前计算的链长(子问题规模) { for(inti=1;i<=n-r+1;i++)//n-r+1为最后一个r链的前边界 { intj=i+r-1;//计算前边界为r,链长为r的链的后边界 m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];//将链ij划分为A(i)*(A[i+1: j]) s[i][j]=i; for(intk=i+1;k { //将链ij划分为(A[i: k])*(A[k+1: j]) intt=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j]; if(t { m[i][j]=t; s[i][j]=k; } } } } returnm[1][L-1]; } voidTraceback(inti,intj,int**s) { if(i==j)return; Traceback(i,s[i][j],s); Traceback(s[i][j]+1,j,s); cout<<"矩阵A"< cout<<"乘矩阵A"<<(s[i][j]+1)<<","< 在计算过程中,保存已解决的子问题的答案。 每个子问题只计算一次,而在后面需要时只需简单检查一下,从而避免了大量的重复计算,最终得到多项式时间的算法。 } 测试结果: 分析: 用动态规划迭代方式解决此问题,可依据其递归式自底向上的方式进行计算。 在计算过程中,保存已解决的子问题的答案。 每个子问题只计算一次,而在后面需要时只需简单检查一下,从而避免了大量的重复计算,最终得到多项式时间的算法。 问题五: 找零问题 思路: 此问题即为背包问题,假设已知如何解决money为i-1,硬币种类为j-1的子问题,记为P(i-1,j-1)。 假设已知如何解决P(i,j-1)。 记Cj=i/j,这是单独使用第j种硬币需要的数量,Vj表示j的面值。 设Rj=i-Cj*Vj,使用Cj后剩余的money;设Qj=i-(Cj-1)*Vj,使用Cj-1个硬币后剩余的money此时Qj=Rj+Vj。 那么接下来找零Rj使用的硬币一定比找零Qj的硬币少吗? 这个不一定,因为Rj相比Qj跟小,这个意味着需要更多的零钱。 比如Rj=2,Qj=12,此时都需要两个硬币(1,1和3,9),而最终结果是开始选择Cj-1比选择Cj的少一个。 所以在这里,应当全面的考察由1,2,…Cj的情况。 P(i,j-1)=MIN{P(i,j-1),P(i-k*Vj,j-1)+k}(k=1,2,…Cj) 复杂度: O(m*n); 源代码: #include usingnamespacestd; intleastnums(intcoins[],intnumcoin,intmoney) { if(numcoin<1||money<0||coins[0]>money) return-1; int*res=newint[money+1]; //初始化,只用第一个硬币 for(inti=1;i res[i]=money/coins[0]; for(intj=1;j { for(inti=coins[j];i { intk=i/coins[j]; if(i%coins[j]==0) { res[i]=k; continue; } while(k) { intleft=i-k*coins[j]; if(res[left]+k res[i]=res[left]+k; --k; } }//endfor }//endfor returnres[money]; } intmain() { intcoins[]={1,2,3,10}; intlnum=leastnums(coins,4,25); cout<<"最少用"< "< return0; } 测试结果: 分析: 完全背包问题,即用最少的物品装最多的价值。 可以转化为0-1背包问题。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 算法 导论 报告
![提示](https://static.bdocx.com/images/bang_tan.gif)