1基础题_2.由计算机生成简单的四则运算题
1.1 需求分析:
本题主要是要求设计一个可以自动生成四则运算的测试器,并且完全由用户决定出加、减、乘、除哪一种运算题,以及出一位数还是两位数的运算题,同时还要对用户给出的答案的对错进行判断。在程序运行过程中,用户可以选择何时结束程序,并在结束程序时给出一个某种形式的成绩。
///////////////////////////////////////////// 程序执行的结果:///////////////////////////////////////////////////
1.2 概要设计:
在对题目理解的基础上,并针对几个特别的技术环节,我认为程序可分为三个部分:
1) 程序的欢迎界面,主要通过一些特殊制表符来完成。其中运行,退出程序可以通过一个while循环来判定同时还要考虑用户输入信号量的正误;
2) 出题函数,也是本程序最关键的一个函数,通过使用“rand()%10”或“rand()%100”来获得一个0到9的一位整数随机值或得到0到99的两位整数随机值来为用户出题,并判断用户答案的对错;
3) 评分系统,是在用户选择退出后对用户所答题情况给出的成绩评价。
///////////////////////////////////////////////////
程序流程图:
1.3 详细设计与编码:
为了使程序更加简洁与工整,且容易修改和阅读,我采用头文件的方式将Exam()函数放在了Exam .h中。Exam()函数主要负责程序的出题和结果的判断,其输入接口为运算符号,位数,即只需向其输入四则运算的一种符号和运算的位数,函数便自动生成题目并自动判断结果的正误,结果以1,0返回。而主程序则是完成了程序的开始、结束,用户成绩的判定。
///////////////////////////////////////////////////
具体源程序如下:
----------------------------------------------------------------------------------------------------------------------
int Exam(int figure, int sign)
{//本函数负责给用户出题
if (figure!=1&&figure!=2&&sign<1&&sign>4) return 0;
//判断函数的输入是否符合要求
int a, b;
if (figure==1)
a=rand()%10; b=rand()%10;
if (figure==2)
a=rand()%100; b=rand()%100;
switch(sign)
{
case(1):
{
cout<<" "<<a<<'+'<<b<<'=';
int r; cin>>r;
if(r!=a+b)
{
cout<<" "<<"╳ 很遗憾,回答错误! X﹏X "<<endl;
return -1;
}
else
{
cout<<" "<<"√ 恭喜你回答正确!↘(^ω^)↙"<<endl;
return 1;
}
}
case(2):
{
cout<<" "<<a<<'-'<<b<<'=';
int r; cin>>r;
if(r!=a-b)
{
cout<<" "<<"╳ 很遗憾,回答错误! X﹏X "<<endl;
return -1;
}
else
{
cout<<" "<<"√ 恭喜你回答正确!↘(^ω^)↙"<<endl;
return 1;
}
}
case(3):
{
cout<<" "<<a<<'*'<<b<<'=';
int r; cin>>r;
if(r!=a*b)
{
cout<<" "<<"╳ 很遗憾,回答错误! X﹏X "<<endl;
return -1;
}
else
{
cout<<" "<<"√ 恭喜你回答正确!↘(^ω^)↙"<<endl;
return 1;
}
}
case(4):
{
while(b==0) b=rand()%10;
cout<<" "<<a<<'/'<<b<<'=';
int r; cin>>r;
if(r!=a/b)
{
cout<<" "<<"╳很遗憾,回答错误! X﹏X "<<endl;
return -1;
}
else
{
cout<<" "<<"√ 恭喜你回答正确!↘(^ω^)↙"<<endl;
return 1;
}
}
}
return 0;
}
----------------------------------------------------------------------------------------------------------------------
main() 见上传程序 Arithmetic . cpp文件
1.4 调试分析:
为了使程序的欢迎界面更加的友好,我采用了制表符来美化程序界面,让整个程序不会显得那么的单调和无趣。在调试过程中,为了使界面好看确实下了很大的功夫。
在调试过程中程序使用"rand()%10"或"rand()%100" 获得的随机数往往不随机,即获得的随机数不变,经过查阅资料和详细分析,原来它需要初始化。
1.5 用户使用说明:
友好的程序界面给人一种亲切感,整个程序使用起来并不是十分的复杂,只需要根据每步的提示进行即可。至于在进行除法运算时,面对无法除尽的数用户只需要保留小数点后一位数字即可。
1.6 设计心得:
设计制作类似的程序已经不是第一次了,但这次却是比以前各次都下了大功夫。虽然整个题目并不是很难,出题函数也比较容易实现,但我抓住了程序界面的设计,让整个程序变得友好而吸引人。然原本枯燥的四则运算测试变得有意思。而且通过这次的编程,使我对设计一个程序的步骤更加的熟悉,为后面的加强题打下了坚实的基础。
运行程序后,看到自己完全独立设计制作的程序,一种无比的自豪感油然而生,内心掩饰不住的喜悦。特别是听到在同学们看到我的程序界面时发出的感叹声时,心里也是无比的开心。
2基础题_6.钱币兑换问题(贪心算法)
2.1 需求分析:
本题主要是要求设计一个程序,让用户输入正整数m ,它代表一个人民币钱数(元数),由程序计算一个最有方法,使人民币纸币的张数最少,并凑成上述的钱数m 。
///////////////////////////////////////////// 程序执行的结果:///////////////////////////////////////////////////
2.2 概要设计:
在对题目理解的基础上,以及题目中所给出的要求,我认为此问题最好还是选用贪心算法会比较方便。而且由于此问题也恰巧是贪心算法的最典型应用,即所求问题的整体最优解可以通过一系列局部最优的选择来达到。
///////////////////////////////////////////////////
程序流程图:
2.3 详细设计与编码:
根据上面的流程图可以看到如果是一步一步的写程序,势必会让程序变得冗长且不易阅读,因而我想到使用循环的方法,将流程图中类似的结构体做成一个循环体来实现,使程序源代码变得十分的简洁,且容易被阅读和修改。
///////////////////////////////////////////////////
具体源程序如下:
----------------------------------------------------------------------------------------------------------------------
void main()
{
cout<<"请输入总钱数 m";
int m=0;
cin>>m;
int N[7]={0,0,0,0,0,0,0}; //用来存储每种面值纸币所需的数目
int Money[7]={100,50,20,10,5,2,1}; //存放7种纸币的面值
for (int i=0; i<7; i++)
{
while (m>=Money[i])
{
m-=Money[i];
N[i]++;
}
}
cout<<"最少使用"<<endl;
for (i=0; i<7; i++)
{
cout<<"\t面值"<<setw(3)<<Money[i]
<<"的纸币"<<N[i]<<"张"<<endl;
}
}
----------------------------------------------------------------------------------------------------------------------
2.4 调试分析:
本程序的调试工作显得非常的简单,只需要对几个特殊的值进行检查就行了。程序输出也是十分的清晰,容易查错。这主要是由于问题并不是十分复杂,再加上贪心算法的简便性。使得整个程序浑然一体。
2.5 用户使用说明:
在程序运行后需要用户输入需要兑换的总钱数,回车后系统便自动将各种纸币所需要的张数输出来。供用户参考。且输出的结果是张数最少的情况。
2.6 设计心得:
钱币兑换问题是个非常简单的题目,完成本题所需的编程技巧并不多,但却巩固了我在算法设计与分析课上所学到的很多知识。特别是对于贪心算法,不但让我对其有了更进一步的了解,而且使我能够更好的掌握在分析问题时,首先对问题的解决算法进行分析的能力。
3基础题_9.约瑟环问题(使用数组存放人员编号)
3.1 需求分析:
本题主要是要求设计一种算法,使用数组来存放n个人,而后从1号人员开始报数(顺时针方向),当数到k时(其中k>1由用户通过cin输入指定),则该号人员被“淘汰出局”;接着仍沿顺时针方向从被淘汰出局者的下一人员又重新从1开始数起,数到k后,淘汰第2个人;如此继续,直到最后剩下一个人时停止。请输出最后所剩那一个人的编号,并输出淘汰过程的某种“中间结果数据”。
///////////////////////////////////////////// 程序执行的结果:///////////////////////////////////////////////////
3.2 概要设计:
在对题目理解的基础上,以及题目中所给出的要求,我认为此问题可以通过模拟指针循环查找的方法来实现题目所给的限定。在淘汰人员时,我准备利用一个布尔数组来存放这n个人的状态(是否被淘汰),然后通过一个point"指针"对其进行循环查找。而另定义一个j变量来进行报数操作。不但可以实现在时下最后一个人时输出这个人的编号,还可以在每次淘汰人员时,输出被淘汰人员的编号。
///////////////////////////////////////////////////
程序流程图:
3.3 详细设计与编码:
因为总人数是由用户所给定的,所以主函数内的所有涉及n的数组都需要使用动态数组来进行定义。整个报数环节在主函数中体现在一个while循环上,而跳出这个循环的条件便是对队列中人数的判断,即当队列中的人数只剩下一个时,跳出此循环。
///////////////////////////////////////////////////
具体源程序如下:
----------------------------------------------------------------------------------------------------------------------
void main()
{
int n, k;
cout<<"请输入总人数 n:";
cin>>n;
cout<<"请输入报数的最大值 k:";
cin>>k;
bool *p=new bool[n+1]; //布尔 p 数组用来存放 n 个人的状态
for (int i=1; i<=n; i++) p[i]=1;//1 状态在队列里 0 被淘汰了
int point=0, j=0, m=n; //point 编号指针 j 报数 m 队列里剩有的人数
while (true)
{
j++; point++;
if (point==n+1) point=1; //point 指向 n+1 是转到1
while (p[point]==0)
{
point++;
if (point==n+1) point=1;
}
if (j==k) //报数报到 k
{
p[point]=0;
cout<<"编号为 "<<point<<" 的人被淘汰了。"<<endl;
m--; j=0;
}
if (m==1) break; //队列只剩下1个人时跳出循环
}
point=1;
while (p[point]==0) point++;
cout<<"最后剩下的人的编号为 "<<point<<" 。"<<endl;
}
----------------------------------------------------------------------------------------------------------------------
3.4 调试分析:
本程序的调试并不怎么复杂,但却有点麻烦。因为为了测得程序运行的正确性,在输入n之前,我总是要先把正确的答案计算出来,再对程序的结果进行比较。其中也难免自己计算错误的时候,况且n的值越大,其产生的结果也就越繁多,因而比较耗时。
在整体的调试下,本程序并没有出现错误,在对每步输出上也是比较完美的,达到了预先设定的效果。
3.5 用户使用说明:
在程序运行后需要用户输入排成一圈的总人数n,接着会让用户输入报数的最大值k,这两个数理论上在大小上没有限制,但输入的必须是整型正整数,否则系统会报错。在程序输出时,每一步所淘汰的成员编号都会被输出,因而用户可以清晰地看到整个的淘汰过程。并可由最后输出的结果知道最后会剩下哪个编号的成员。
3.6 设计心得:
约瑟环问题是一个比较老的问题了,在刚接触C++语言时就做过类似的题目,因而完成起来并没有太大难度。虽然在题目中对设计时所使用的数据结构进行了限制,但这并不是本题的难点,我认为关键在于超越自我,设计出与我过去编程时所设计出的程序有所不同。我觉得我做到了,这次的程序是我完全重新设计的,从算法到数据结构的使用均是独立完成,感到很有成就感。
4基础题_11.猜扑克牌问题
4.1 需求分析:
本题主要是要求设计一个程序,能让计算机来猜测用户“暗记”的某张扑克牌:计算机从一副扑克牌(54张)中任意抽出27张,摆放在不同的三行上(每行9张),用户“暗记”某张纸牌,而后告诉计算机所“暗记”的那张纸牌处于哪一行中;之后计算机再两次将纸牌重新摆放,并让用户再回答两次相同的提问(那张纸牌在重新摆放后又处在哪一行上);此时计算机会将用户所“暗记”的那张纸牌给挑出来。
///////////////////////////////////////////// 程序执行的结果:///////////////////////////////////////////////////
4.2 概要设计:
在对题目理解的基础上,以及题目中所给出的要求,我认为此问题有以下两个重点:
1)要从一副54张的扑克牌中任意抽出27张,可通过“rand()%54”所产生的随机值来确定。但注意,一旦随机抽走哪张,下次牌中就没有这张了。
2)程序总按照一种策略将三行纸牌重新“摆放”,而后进一步让用户进行指定。上述所谓的策略指的是,总将纸牌“一分为三”:第一次要将每一行的9张分散到不同的3行上(每行仅“剩”3张),而第二次则要将上次“确定”出的某3张进一步分散到不同的3行上(每行只“剩”1张。此时靠用户再指定一次行号则可唯一确定所“暗记”的那张纸牌)。
///////////////////////////////////////////////////
程序流程图:
4.3 详细设计与编码:
题目中有很多地方都涉及到无序排列的问题,再加上一旦随机抽走哪张,下次牌中就没有这张了,而且在分组完成后数据的存放都是一个问题。所以我采用布尔变量数组来存放牌的状态(抽走了为0,未抽走为1)。而使用string数组来存放每一张牌。在输出的问题上,我将无须输出和有序输出函数单独做成一个头文件:OutWay.h ,以方便函数的调用于阅读。为解决题目的最终问题,在流程图中的交换操作需要下一些功夫才能很好的完成。
///////////////////////////////////////////////////
具体源程序如下:
----------------------------------------------------------------------------------------------------------------------
void main()
{
srand(GetTickCount()); //使rand()函数每次所取得随机数不同
string squeezer[54]={"\3- A","\3- 2","\3- 3","\3- 4","\3- 5","\3- 6","\3- 7","\3- 8","\3- 9",
"\3-10","\3- J","\3- Q","\3- K","\4- A","\4- 2","\4- 3","\4- 4","\4- 5",
"\4- 6","\4- 7","\4- 8","\4- 9","\4-10","\4- J","\4- Q","\4- K","\5- A",
"\5- 2","\5- 3","\5- 4","\5- 5","\5- 6","\5- 7","\5- 8","\5- 9","\5-10",
"\5- J","\5- Q","\5- K","\6- A","\6- 2","\6- 3","\6- 4","\6- 5","\6- 6",
"\6- 7","\6- 8","\6- 9","\6-10","\6- J","\6- Q","\6- K","KING1","KING2"};
//顺序列出54张牌
cout<<"The 54 cards aring in proper order is arranged as follows: "<<endl;
Order_Out(squeezer, 54);
string draw[27]; //存放随机抽取出来的27张牌
string line1[9], line2[9], line3[9];
//分别存放从27张牌中抽取出的9张牌
bool a1[54], a2[27];
//状态参数,a1用来存放第一次随机抽取时54张牌的状态
//a2用来存放第二次随机抽取时27张牌的状态
for (int i=0; i<54; i++)
a1[i]=true;//状态初始化
for (i=0; i<27; i++)
a2[i]=true;//状态初始化
int b; //用来产生随机数
for (i=0; i<27; i++)
{
b=rand()%54; //产生0~53之间的随机数
while(a1[b]==false)
b=rand()%54; //产生0~53之间的随机数
a1[b]=false;
draw[i]=squeezer[b];
}
cout<<"The 27 cards sampled are arranged as follows: "<<endl;
Order_Out(draw, 27); //顺序列出抽取出的27张牌
for (i=0; i<9; i++)
{
b=rand()%27; //产生0~27之间的随机数
while(a2[b]==false)
b=rand()%27; //产生0~27之间的随机数
a2[b]=false;
line1[i]=draw[b];
}
for (i=0; i<9; i++)
{
b=rand()%27; //产生0~27之间的随机数
while(a2[b]==false)
b=rand()%27; //产生0~27之间的随机数
a2[b]=false;
line2[i]=draw[b];
}
for (i=0; i<9; i++)
{
b=rand()%27; //产生0~27之间的随机数
while(a2[b]==false)
b=rand()%27; //产生0~27之间的随机数
a2[b]=false;
line3[i]=draw[b];
}
cout<<"-------------------------------------"<<endl
<<"line1:";
Orderless_Out(line1, 9); //无序输出line1
cout<<"line2:";
Orderless_Out(line2, 9); //无序输出line2
cout<<"line3:";
Orderless_Out(line3, 9); //无序输出line3
cout<<"-------------------------------------"<<endl
<<"Remember a card, and tell me what line it reside in (1/2/3): ";
int c; //用户输入的信号量
cin>>c;
while (c<1||c>3)
{
cout<<"Sorry, your importation contain mistake, please reinput: ";
cin>>c;
}
switch(c)
{//将用户选出的一行分成3份,分布到每一行
case 1:
{
for (i=3; i<6; i++)
swap(line1[i], line2[i]);
for (i=6; i<9; i++)
swap(line1[i], line3[i]);
break;
}
case 2:
{
for (i=0; i<3; i++)
swap(line2[i], line1[i]);
for (i=6; i<9; i++)
swap(line2[i], line3[i]);
break;
}
case 3:
{
for (i=0; i<3; i++)
swap(line3[i], line1[i]);
for (i=3; i<6; i++)
swap(line3[i], line2[i]);
break;
}
}
cout<<"-------------------------------------"<<endl
<<"line1:";
Orderless_Out(line1, 9); //无序输出line1
cout<<"line2:";
Orderless_Out(line2, 9); //无序输出line2
cout<<"line3:";
Orderless_Out(line3, 9); //无序输出line3
cout<<"-------------------------------------"<<endl
<<"What line the card you remenbered reside in now (1/2/3): ";
cin>>c;
while (c<1||c>3)
{
cout<<"Sorry, your importation contain mistake, please reinput: ";
cin>>c;
}
switch(c)
{//将用户选出的一行中的3个可能值,分布到每一行的首位
case 1:
{
swap(line1[1], line2[0]);
swap(line1[2], line3[0]);
break;
}
case 2:
{
swap(line2[3], line1[0]);
swap(line2[4], line2[0]);
swap(line2[5], line3[0]);
break;
}
case 3:
{
swap(line3[6], line1[0]);
swap(line3[7], line2[0]);
swap(line3[8], line3[0]);
break;
}
}
cout<<"-------------------------------------"<<endl
<<"line1:";
Orderless_Out(line1, 9); //无序输出line1
cout<<"line2:";
Orderless_Out(line2, 9); //无序输出line2
cout<<"line3:";
Orderless_Out(line3, 9); //无序输出line3
cout<<"-------------------------------------"<<endl
<<"What line the card you remenbered reside in now (1/2/3): ";
cin>>c;
while (c<1||c>3)
{
cout<<"Sorry, your importation contain mistake, please reinput: ";
cin>>c;
}
//用户所输入的行数的第一张牌即用户一开始就“暗记”的牌
if (c==1)
cout<<"Your remenber card is: "<<line1[0]<<endl;
else if (c==2)
cout<<"Your remenber card is: "<<line2[0]<<endl;
else cout<<"Your remenber card is: "<<line3[0]<<endl;
}
----------------------------------------------------------------------------------------------------------------------
其余函数请查看头文件OutWay.h
----------------------------------------------------------------------------------------------------------------------
4.4 调试分析:
本程序是一个比较有意思的程序,在调试的过程中,确认结果比较容易,但要想完全检测程序是否有误,还是比较繁的。不过整个程序调试下来,还是挺好玩的,看到自己心里“暗记”的牌最后可以被计算机找出来。不过我的程序还有很大的改进空间,如将switch语句进行调整,或合并成一个大循环等。
4.5 用户使用说明:
在程序运行后需要用户根据程序的提示,在抽出的27张牌中“暗记”一张牌,然后3次回答程序“暗记”的那张牌的行数,程序便能够自动将用户所“暗记”的牌显示出来。
4.6 设计心得:
这一题是这么多题目中最有意思的一道题,向变魔术一般,将用户“暗记”的牌展示出来。整个程序还是比较复杂的,特别是我在设计输出函数时,无序排列确实让我费了一番周折。而且在这3个switch语句的case语句中的交换也是比较容易弄混的,我就在编程序是忘了在case语句后加上break而导致程序一直调试不成功,好不容易才改正过来。
5基础题_13. 找出整数的前第n位和后第n位
5.1 需求分析:
本题主要是要求设计具有如下原型的函数:int f(unsigned long x, int n, int& Lxn); 它负责将整数x的第n位(从左边数第n位,n>0)的数值放到引用Lxn之中(将作为结果返回到主调函数的对应实参变量中),并将倒数第n位(从右边数第n位,n>0)的数值作为函数结果返回去。并编制主函数对它进行调用以验证其正确性。
///////////////////////////////////////////// 程序执行的结果:///////////////////////////////////////////////////
5.2 概要设计:
在对题目理解的基础上,以及题目中所给出的要求,我认为此问题有以下两个重点:
1)将用户输入的这个长整型的数按位存储在一个数组里;
2)通过循环输出这个数组的第n-1项和倒数第n-1项。
///////////////////////////////////////////////////
程序流程图:
5.3 详细设计与编码:
当x=123456789,n=7时,执行语句“Rxn=f(x, n, Lxn);”将使返回的Lxn为7,并使Rxn变为3;而执行语句“Rxn=f(12345, 6, Lxn);”将使Lxn与Rxn都变为为0(超出数的“长度”即总位数时返回0)。
///////////////////////////////////////////////////
具体源程序如下:
----------------------------------------------------------------------------------------------------------------------
int f(unsigned long x, int n, int&Lxn)
{//题目所要求的函数f
int N=1; //用来记录无符长整数x的位数
unsigned long IM=x/10;
while (IM!=0)
{
IM=IM/10;
N++;
}
if (n>N)
{//用来判断n是否超出x的长度
Lxn=0;
return 0;
}
int *p=new int[N+1]; //用来存放x的每一位数
for (int i=N; i>0; i--)
{//将x的每一位数字倒序的输入到数组p中
int j=pow(10,(N-i+1));
p[i]=(x%j-x%(j/10))/(j/10);
}
Lxn=p[n]; //返回引用Lxn的值
return p[N-n+1]; //函数返回的值
}
----------------------------------------------------------------------------------------------------------------------
main()函数见test_Main .cpp
----------------------------------------------------------------------------------------------------------------------
5.4 调试分析:
本程序在调试的过程中,可能会遇到由于长整型数位数的限制,用户如果输入的x太大的话便会产生程序的溢出错误,造成程序进入死循环。所以在输入时,x的大小因有所限制。
5.5 用户使用说明:
在程序运行后需要用户根据程序的提示输入一个长整型的x,在输入要显示的位数n即可让程序将x的第n位和从后数第n位显示出来。
5.6 设计心得:
这道题重点便是怎样将用户输入的长整型数分位存储,当然,我们也可以选用getchar()的方式,但我个人认为还是使用数组来通过取余的方式将x的每一位村与数组中比较好。
在完成程序后,调试是发现了很多的错误,其中取余的地方就一直通不过,在反复调试后才最终将程序改好。通过这次编程使我懂得即使是非常有把握的程序,如果不细心的话,一样会碰钉子。
6基础题_14.整数组前n项是否按降序排列
6.1 需求分析:
本题主要是要求编出两种算法,一种递归一种非递归两种函数f,负责判断数组a的前n个元素是否从大到小完全有序了,是则返回true,否则返回false。
///////////////////////////////////////////// 程序执行的结果:///////////////////////////////////////////////////
6.2 概要设计:
1)非递归函数中只需逐对地判断各a[i] 与a[i+1]是否都已从大到小有序排列(i = 0,1,…,n-2)。
2)递归函数中将问题分解处理为:若n=1(即只有1个元素时)则返回true而递归出口;n>1时,若最后一对元素不顺序则返回false,否则进行递归调用(传去实参a与 n-1,去判断前n-1个元素的顺序性),并返回递归调用的结果(与前n-1个元素的是否顺序性相同)。
///////////////////////////////////////////////////
程序流程图: