这是我当年实习的实习报告,写的不算好,当时数据结构没学好,不会用链表这些东西。
第一部分:基础问题
1.2
1.实验题目
17世纪法国数学家加斯帕在《数学的游戏问题》中讲的一个故事:n个教徒和n个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了个办法:2n个人围成一个圆圈,从第一个人开始依次循环报数,每数到第九个人就将他扔入大海,如此循环直到仅剩n个人为止 。问怎样的排法,才能使每次投入大海的都是非教徒。
2.需求分析
本演示程序用VC编写,完成从文件中输入任意的整数n,并把排好序的字符串输出到一个文件中。
① 输入的形式和输入值的范围:输入文件由一行构成,就是n的值。其中n是整数。
② 输出的形式:输出文件中是一行字符串,字符串由n个‘@’字符(代表教徒)和n个‘+’ 字符(代表非教徒)排列构成。该排列使得按照前面的约定每次投入大海的都是非教徒。
③ 程序所能达到的功能:完成从文件中输入任意的整数n,并把排好序的字符串输出到 一个文件中。该排列使得按照前面的约定每次投入大海的都是非教徒。
④ 测试数据:
输入文件中n=15,输出文件为@@@@+++++@@+@@@+@++@@+++@++@@+
3.概要设计
1)为了实现上述程序功能,需要定义一个数组:
2)本程序包含1个函数:
① 主函数main()
4.详细设计
实现概要设计中定义的所有数据类型,对每个操作只需要写出伪码算法。
1) 数组类型
#define N 100
int A[N] ;
2) 主函数的主要操作伪码算法
首先从文件中读取n,然后在数组中赋值2*n个64(‘@’的ASCLL值为64)
for(z=0;z A[z]=64; 然后对数组从头到尾遍历,除了‘+’每到第九个就赋值为34(‘+’的ASCLL值为34),直到赋了n个‘+’为止。 最后把数组中的值一一输出到文件中。 fp=fopen("b.txt","w"); for(z=0;z fprintf(fp,"%c ",A[z]); fclose(fp); 5.调试分析 在运行中主要遇到逻辑错误一步步的通过调试找出来。 6.使用说明 程序名为1.2.exe,运行环境为Windows。 在输入文件a.txt中存储一个整型n值。 程序执行完后会自动生成b.txt文件中有排好序的字符串。 7.测试结果 8.附录 源代码如下: #include #define N 100 int main() { int i=1,j,z,v=0,g=1; //g表示需要投进海的非教徒,通过i来遍历到第九个人投海。 int n,m; //n表示教徒或者非教徒的人数,m来表示教徒和非教徒总数 FILE *fp; fp=fopen("a.txt","r"); //打开一个a.txt文件用于只读 if(fp==NULL) return -1; while(!feof(fp)) { fscanf(fp,"%d",&n); //在文件中读取n的值 } m=2*n; fclose(fp); //读取完后关闭文件 int A[N]; for(z=0;z A[z]=64; while(g<=n) //非教徒还没全部投海则继续,否则停止 { for(j=v;j<=m-1&&i<=8;) //通过i来遍历到第九个人投海 { if(A[j]==43) //如果A[j]=='+'时,j++ { if(j==m-1) //如果A[j]=='+'&&j=m-1时,j=0从头开始遍历 j=-1; j++; } elseif(j==m-1) //如果A[j]!='+'&&j==m-1,则j=0从头开始 { i++;j=0; } else //如果A[j]!='+'&&j!=m-1,则i++,j++ { i++;j++; } } //当i到第九个时 if(i==9&&A[j]!=43) //当i到第九个且这个数是'@' { A[j]=43; //对其赋值'+' if(j==m) v=0; else v=j+1; i=1; g++; } else if(i==9&&A[j]==43) //当i到第九个且这个数是'+' { while(A[j+1]==43&&j<=28) //则一直往下查找是'@'时对其赋值 { j++; if(j==m)j=0; } A[++j]=43; if(j==m) v=0; else v=j+1; i=1; g++; } } fp=fopen("b.txt","w"); //执行完后创建一个只写文件b.txt for(z=0;z fprintf(fp,"%c ",A[z]); //把数组中的字符串输出到文件b.txt中 fclose(fp); //执行完后关闭文件 return 0; } 1.3 1.实验题目 我们要求找出具有下列性质数的个数(包含输入的自然数n)。先输入一个自然数n(n<=1000),然后对此自然数按照如下方法进行处理: 1.不作任何处理; 2.在它的左边加上一个自然数,但该自然数不能超过原数最高位数字的一半; 3. 加上数后,继续按此规则进行处理,直到不能再加自然数为止。 2.需求分析 本演示程序用VC编写,完成题目要求输入一个自然数n,然后输出满足三个条件的数的个数。 ①输入的形式和输入值的范围:自然数n,并使n<=1000。 ②输出的形式:整数。 ③程序所能达到的功能:输出满足条件的数的个数。 ④测试数据:输入6 输出6 3.概要分析 1)为了实现上述程序功能,需要用一个while循环找出最高位数字和一个switch语句来进行求满足条件的数的个数: 2)本程序包含1个函数: ① 主函数main() 4.详细设计 实现概要设计中定义的所有数据类型,对每个操作只需要写出伪码算法。 1)定义一个整型数N存放输入数据; 2)找出整型数N的最高位数字n n=N; while(n/10!=0) { n=n/10;} 3)当n=1时,求得满足条件的数有1; 当n=2时,求得满足条件的数有2,12; 当n=3时,求得满足条件的数有3,13; 当n=4时,求得满足条件的数有4,14,24,124; 当n=5时,求得满足条件的数有5,15,25,125; …… 所以有如下switch语句来求满足条件的个数 switch(n/2) { case 0:num+=0;break; case 1:num+=1;break; case 2:num+=3;break; case 3:num+=5;break; case 4:num+=9;break; } printf("%d\n",num+1); 5.调试分析 在调试过程中发现最高位2和3是同样的个数,所以后来在switch中把switch(n)改为了switch(n/2),大大简化的程序。 6.使用说明 程序名为1.3.exe,运行环境为Windows。程序执行后显示 请输入一个不大于1000的整数: 然后在光标出输入一个不大于1000且不以0开头(除了0)的数字,再按回车将会显示执行后的结果。 7.测试结果 8.附录 源代码如下: #include int main() { int N,n,num=0; //N代表输入的值,num统计满足条件的个数并赋初值0 printf("请输入一个不大于1000的整数N\n"); scanf("%d",&N); n=N; while(n/10!=0) //找出最高位的数字 { n=n/10;} switch(n/2) //通过最高位的数字一半来分类处理 { case 0:num+=0;break; case 1:num+=1;break; case 2:num+=3;break; case 3:num+=5;break; case 4:num+=9;break; } printf("%d\n",num+1); //输出满足条件的个数 return 0; } 1.5 1.实验题目 蛇形矩阵是由1开始的自然数依次排列成的一个矩阵上三角形。 2.需求分析 要求打印出一个不大于100阶的矩阵上三角形,且打印的数字由1开始的自然数。 (1) 输入的形式和输入值的范围:本题有多组数据,每组数据由一个正整数N组成。(N不大于100) (2) 输出的形式:对于每一组数据,输出一个N行的蛇形矩阵。两组输出之间不要额外的空行。矩阵三角中同一行的数字用一个空格分开。行尾不要多余的空格。 (3) 程序所能达到的功能:完成一个蛇形矩阵的输出。 (4) 测试数据:输入5 输出 1 3 6 10 15 2 5 914 4 8 13 7 12 11 3.概要设计 为了实现上述程序功能,需要定义一个二维数组: a[100][100]; 本程序包含1个函数: 主函数main(); 4.详细设计 实现概要设计中定义的所有数据类型,对每个操作给出伪码算法。对主程序写出伪码算法。 1)定义一个整型二维数组存放蛇形矩阵中的值 a[100][100]; 2)通过while来控制多组数据输入 while(scanf("%d",&N)) 3)由蛇形矩阵的规律从左下角往右上角的遍历有如下算法 s=1; a[0][0]=s; for(k=1;k<=N-1;k++) for(i=k,j=0;i>=0,j<=k;i--,j++) //通过i--,j++来实现这个规律 { a[i][j]=s+1; s++; } 4)给N阶蛇形矩阵输完值后再输出蛇形矩阵 for(i=0;i { for(j=0;j { if(j printf("%d ",a[i][j]); else printf("%d",a[i][j]); } printf("\n"); g--; } 5.调试分析 一开始没有找出这个遍历顺序导致输出结果并不是要求的,后来进过深入的对问题的分析,再转化为简洁的一个二重循环就能遍历出蛇形矩阵。 6.使用说明 程序名为1.5.exe,运行环境为Windows。程序执行后显示 输入一个N,再回车将会输出一个N阶的蛇形矩阵。 再接着还可以输入N,直到输入0为止。 7.测试结果
8.附录 源代码如下: #include intmain() { int s,N,i,j,k,a[100][100],g; //s用来存放自然数,定义了一个二维数组 while(scanf("%d",&N)) //控制多组数据测试,直到输入0为止 { if(N==0) return 0; //如果输入值为0,则终止程序 else { s=1; //对自然数赋初值1 a[0][0]=s; for(k=1;k<=N-1;k++) //通过二重循环来给蛇形矩阵随着s的增加来赋值 for(i=k,j=0;i>=0,j<=k;i--,j++) //通过k控制没次行的开始行数,i--,j++来//使从左下角到右上角的遍历; { a[i][j]=s+1; s++; } g=N; for(i=0;i { for(j=0;j { if(j printf("%d ",a[i][j]); else printf("%d",a[i][j]); } printf("\n"); //没输出一行后换行 g--; } } } return 0; } 1.6 1.实验题目 输出7 和7 的倍数,还有包含7 的数字例如(17,27,37,...,70,71,72,73,...) 2.需求分析 要求输入一个整数N(N不大于30000),要求输出从小到大排列的不大于N的与7有关的数字,每行一个。 (1) 输入的形式和输入值的范围:输入一个整数N(N不大于30000); (2) 输出的形式:输出从小到大排列的不大于N的与7有关的数字,每行一个; (3) 程序所能达到的功能:输出所有与7有关的数字; (4) 测试数据: 输入20 输出7 14 17 3.概要设计 为了实现上述程序功能,需要设计一个for循环还遍历寻找与7有关的数字。 本程序包含1个函数: 主函数main(); 4.详细设计 实现概要设计中定义的所有数据类型,对每个操作给出伪码算法。 1) 定义一个整数N,通过键盘输入N 2) 通过for循环依次找与7有关的数字 for(i=1;i<=N;i++) //从1到N排列的不大于N的与7有关的数字,每行一个。 { if(i%7==0) //i是否是7的倍数 printf("%d\n",i); else if(i%10==7) //i是否个位上含有7 printf("%d\n",i); else if(i/10%10==7) //i是否十位上含有7 printf("%d\n",i); else if(i/100%10==7)//i是否百位上含有7 printf("%d\n",i); else if(i/1000%10==7) //i是否千位上含有7 printf("%d\n",i); } 5.调试分析 在设计程序时有时个、十、百、千位数字描述的不正确,导致程序终止或得到错误结果。 6.使用说明 程序名为1.6.exe,运行环境为Windows。程序执行后显示 请输入一个整数N,使N小于等于3000 输入一个满足条件的N,按回车,将会在屏幕上显示1到N之间的与7有关的数字。 7.测试结果
8.附录 源代码如下: #include intmain() { int N,i; printf("请输入一个整数N,使N小于等于3000\n"); scanf("%d",&N); for(i=1;i<=N;i++) //从1到N排列的不大于N的与7有关的数字,每行一个。 { if(i%7==0) //i是否是7的倍数 printf("%d\n",i); else if(i%10==7) //i是否个位上含有7 printf("%d\n",i); else if(i/10%10==7) //i是否十位上含有7 printf("%d\n",i); else if(i/100%10==7)//i是否百位上含有7 printf("%d\n",i); else if(i/1000%10==7) //i是否千位上含有7 printf("%d\n",i); } return 0; } 1.7 1.实验题目 第四平方和定理,又称为拉格朗日定理:每个正整数都可以表示为至多4个正整数的平方和。如果把0包括进去,就正好可以表示为4个数的平方和。比如:5 = 0^2 + 0^2 + 1^2 + 2^2 ,7 = 1^2 + 1^2 + 1^2 + 2^2 (^符号表示乘方的意思)对于一个给定的正整数,可能存在多种平方和的表示法。要求你对4个数排序: 0 <= a <= b <= c<= d 并对所有的可能表示法按 a,b,c,d 为联合主键升序排列,最后输出第一个表示法 2.需求分析 求第四平方和定理,要求输入一个正整数N (N<5000000),是N=a*a+b*b+c*c+d*d,对a,b,c,d排序,依次输出从小到大的四位数。 (1) 输入的形式和输入值的范围:输入整数N (N<5000000); (2) 输出的形式:4个非负整数,按从小到大排序,中间用空格分开; (3) 程序所能达到的功能:求第四平方定理; (4) 测试数据:输入5 输出0 0 1 2 输入773535 输出1 1 267 838 3.概要设计 本程序需要设计一个四重循环来依次查找满足条件的第一组数。 本程序包含1个函数: 主函数main() 4.详细设计 实现概要设计中定义的所有的数据类型,对每个操作给出伪码算法。 1) 定义一个整数N;定义a,b,c,d分别代表四个数的平方和; 2) 为了查找一组从小到大排列的数使用四重循环 for(a=0;a*a { for(b=a;b*b { for(c=b;c*c { for(d=c;d*d { if(a*a+b*b+c*c+d*d==N) { printf("%d%d %d %d\n",a,b,c,d); return 0; } } } } } 5.调试分析 一开始我的程序为 for(a=0;a<5000000;a++) { for(b=a;b<5000000&&b>=a;b++) { for(c=b;c<5000000&&c>=b;c++) { for(d=c;d<5000000&&d>=c;d++) { if(a*a+b*b+c*c+d*d==N) {printf("%d %d %d%d\n",a,b,c,d); return 0;} } } } } 由于这样a,b,c,d遍历的次数非常之大,也没有并要,时间复杂度太大了,后来改了条件才使773535这个数的四个非负整数输出来。 6.使用说明 程序名为1.7.exe,运行环境为Windows。程序执行后显示 请输入一个小于5000000的整数N 输入一个满足条件的整数N,回车 将会在屏幕上显示想要的结果。 7.测试结果
8.附录 源代码如下: #include int main() { int N,a,b,c,d; printf("请输入一个小于5000000的整数N\n"); scanf("%d",&N); for(a=0;a*a { for(b=a;b*b { for(c=b;c*c { for(d=c;d*d { if(a*a+b*b+c*c+d*d==N) {printf("%d %d %d%d\n",a,b,c,d); return 0;} //输出一组则退出程序运行 } } } } return 0; } 1.8 1.实验题目 有一长度为N(1<=N<=10)的地板,给定两种不同瓷砖:一种长度为1,另一种长度为2,数目不限。要将这个长度为N的地板铺满,一共有多少种不同的铺法? 例如,长度为4的地面一共有如下5种铺法: 4=1+1+1+1 4=2+1+1 4=1+2+1 4=1+1+2 4=2+2 编程用递归的方法求解上述问题。 2.需求分析 本演示程序用VC 编写,完成对一个长度为N的地面各种铺瓷砖的方法的总数 (1) 输入的形式和输入值的范围:输入整数N (1<=N<=10); (2) 输出的形式:输出一个数,代表所有不同的瓷砖铺放方法的总数; (3) 程序所能达到的功能:求长度为N的地板瓷砖的铺法总数; (4) 测试数据:输入4 输出5 3.概要设计 1)为了实现上述程序功能,需要定义一个整数N代表地板长度 2)本程序包含2个函数: 1.主函数main() 2.递归函数求方法总数f() 各函数间关系如下: main——>f 4.详细设计 实现概要设计中定义的所有数据类型,对每个操作给出伪码算法。 1)地板长度N 2)函数int f(int n) { if(n>=3) returnf(n-1)+f(n-2); else return n; } 3)函数调用并输出结果 printf("%d\n",f(N)); 5.调试分析 当n=1或2时,方法总数就是n。 而且他们满足f(n)=f(n-1)+f(n-2); 所以可以用递归来描述这个程序更为简洁。 5.调试分析 对于n等于1或2时方法就为n。 6.使用说明 程序名为1.8.exe,运行环境为Windows。程序执行后显示 请输入大于等于1小于等于10的一个地板长度整数N: 输入一个N,回车 屏幕上将会显示满足条件的方法数。 7.测试结果
8.附录 源代码如下: #include intf(int n) { if(n>=3) return f(n-1)+f(n-2); //斐波那契数列递归 else return n; //当n<=2时,返回n } intmain() { int N; printf("请输入大于等于1小于等于10的一个地板长度整数N:"); scanf("%d",&N); printf("%d\n",f(N)); return 0; } 第二部分:经典算法 一、 分治法 1.实验题目 编写自然归并合并排序算法:对于初始给定的数组,通常存在多个长度大于1的已自然排好序的子数组段.例如,若数组a中元素为{4,8,3,7,1,5,6,2},则自然排好序的子数组段有{4,8},{3,7},{1,5,6},{2}.用一次对数组a的线性扫描就足以找出所有这些排好序的子数组段,然后将相邻的排好序的子数组段两两合并,构成更大的排好序的子数组段({3,4,7,8},{1,2,5,6}).继续合并相邻排好序的子数组段,直至整个数组已排好序 2.需求分析 本演示程序用VC 编写,完成自然归并合并排序算法。 (1)输入的形式和输入值的范围:输入一个大小为n的整型自然数数组; (2) 输出的形式:输出从小大排列的自然数; (3) 程序所能达到的功能:实现对自然有序的数据排序; (4) 测试数据:输入4,8,3,7,1,5,6,2 输出1,2,3,4,5,6,7,8 3.概要设计 1)为了实现上述程序功能,需要定义一个两个整型数组,分别存放输入数据和每一自然有序的小段开始在数组中的下标值,还要定义一个整型数m存放有多少段自然有序的小段。 2)本程序包含6个函数: ①主函数main() ②对两段相邻的片段排序函数Merge () ③输出数组函数Print () ④线性扫描函数f () ⑤两两合并相邻函数MergePass(() ⑥合并排序函数MergeSort () 各函数间关系如下: Merge Print main f MergePass MergeSort 4.详细设计 实现概要设计中定义的所有的数据类型,对每个操作给出伪码算法。对主程序和其他模块也都需要写出伪码算法。 1) 数组类型 int m; int t[N]; 2) 自然合并排序算法的基本操作 为了程序的操作,把t数组和m变量定义为全局变量。 void Merge(int c[],int d[],int left,int middle,intright) { inti=left,j=middle+1,k=right; while((i<=middle)&&(j<=right)) { if(c[i]<=c[j]) d[left++]=c[i++]; else d[left++]=c[j++]; } if(i>middle) for(intq=j;q<=right;q++) d[left++]=c[q]; else for(intp=i;p<=middle;p++) d[left++]=c[p]; } void Print(int a[],int n) { for(inti=0;i cout< } void f(int a[],int b[],int n,int &m) { int j=0; b[j++]=0; for(inti=0;i { b[j++]=i+1; } m=j; Print(b,j); } void MergePass(int x[],int y[],int s,int n) { int i=0; while(i<=m-2*s) { intr=((i+2*s) Merge(x,y,t[i],t[i+s]-1,r-1); i=i+2*s; } if(i+s Merge(x,y,t[i],t[i+s]-1,n-1); else if(i { for(intj=t[i];j<=n-1;j++) y[j]=x[j]; } } void MergeSort(int a[],int n) { int *b=new int[n]; int s=1; while(s { MergePass(a,b,s,n); s+=s; MergePass(b,a,s,n); s+=s; } delete[] b; } 3) 其他模块伪码算法 int main() { int n,a[N],i; printf("请输入要排序的个数:"); scanf("%d",&n); printf("请输入%d个自然数:",n); for(i=0;i scanf("%d",&a[i]); Print(a,n); f(a,t,n,m); MergeSort(a,n); Print(a,n); return 0; } 5.调试分析 通过对每一个小模块调试测试成功后,再对整个代码调试测试,直到正确为止。 6.使用说明 程序名为自然合并排序算法.exe,运行环境为Windows,程序执行后显示: 请输入要排序的个数: 输入n,回车。 请输入n个自然数: 输入n个自然数,回车。 屏幕上将会在第一行输出排序前的数组,第二行输出各个小片段开始的数组下标值,第三行输出排序后的数组。 7.测试结果 8.附录 源代码如下: #include using namespace std; #define N 100 int m; //自然合并排序中线性扫描中自然有序的段数,已初始化为0 int t[N]; //全部初始化为0 void Merge(int c[],intd[],int left,int middle,int right) //合并c[left:middle]和c[middle+1:right]到d[left:right] { int i=left,j=middle+1,k=right; while((i<=middle)&&(j<=right)) { if(c[i]<=c[j]) d[left++]=c[i++]; else d[left++]=c[j++]; } if(i>middle) for(int q=j;q<=right;q++) //i>middle说明i已经复制到d中了,所以要把剩下的j复制到b中,并且此时c中元素是有序的,所以可以直接复制 d[left++]=c[q]; else for(int p=i;p<=middle;p++) d[left++]=c[p]; } void Print(int a[],intn) //输出数组函数 {