经过大约一周的时间,程序设计A课程逐渐接近了尾声。下面将介绍一下心得。
T1:输入一个百分制的成绩t,将其转换成对应的等级。这道题并不难,我们只需要利用条件语句if就能轻而易举的完成这道题。另外,作为第一题,这道题并没有出现所谓的陷阱。下面让我们看一下源代码: #include<iostream>using namespace std;int main(){ int a; while(cin >>a) { if(a>=90&&a<=100) cout <<'A'<<endl; else if(a>=80&&a<=89) cout <<'B'<<endl; else if(a>=70&&a<=79) cout <<'C'<<endl; else if(a>=60&&a<=69) cout <<'D'<<endl; else if(a>=0&&a<=59) cout <<'E'<<endl; else cout <<"Score is error!"<<endl; }
T2: “水仙花数”是指一个三位数,它的各位数字的立方和等于其本身,比如:153=1^3+5^3+3^3。现在要求输出所有在m和n范围内的水仙花数。
这道题也是一道简单题,我们只需将这个三位数的每一位单独取出来,然后代入公式进行判断就可以了,在解题上并无难处。不过,这道题却是有一个坑:在最后一个水仙花数找到并输出后,不能有空格跟在后面,正是这一点在我做题是卡了我一下。不过并不是什么困难的问题。原代码如下:#include<iostream>using namespace std;int main(){ int m,n,a,b,c,d=0,e; while(cin >>m>>n) { e=1; d=0; for(int i=m;i<=n;i++) { a=i%10; b=i/10%10; c=i/100%10; if(i==a*a*a+b*b*b+c*c*c) { if(e==1) cout <<i; else cout <<' '<<i; e++; } else d++; } if(d==n-m+1) cout <<"no"<<endl; else cout <<endl; }}
T3:对于表达式n^2+n+41,当n在(x,y)范围内取整数值时(包括x,y)(-39<=x<y<=50),判定该表达式的值是否都为素数。
这道题关键在于如何判断【x,y】内所有数是否为素数以及如何判断素数。首先我们要知道,何为素数?素数即除1和自己外无其他因数。因而在判断n时要从2判断到n-1,但实际上,我们还可以进行优化,只需从2判断到sqrt(n)即可。如何判断所有的数呢?下面我们看代码:#include<iostream>#include<math.h>using namespace std;int main(){ int m,n,a,b; while(cin >>m>>n) { if(m==0&&n==0) break; b=0; for(int i=m;i<=n;i++) { a=i*i+i+41; for(int j=2;j<=sqrt(a);j++) { if(a%j==0) b++; }} if(b==0) cout <<"OK"<<endl; else cout <<"Sorry"<<endl; }}
不难看出,我运用了b这个细节,本质上b的用法和bool型变量是一致的(只是我不会用后者)。当出现一个数不为素数时,b的值会加1,判断结束后,只要b不等于0,说明这一组数不全是素数。
T4:有一个长度为n(n<=100)的数列,该数列定义为从2开始的递增有序偶数,现在要求你按照顺序每m个数求出一个平均值,如果最后不足m个,则以实际数量求平均值。编程输出该平均值序列。
个人认为,这个题还是有一定的难度的。至少我是思考了一整天才理清了思路。如果n可以被m整除,那么我们只需每m个数求一个平均数;不能整除,我们依旧可以先求出可以被整除的那一部分,对于最后不能被整除的部分,单独进行考虑。因而我们可以将问题分为三类进行解决。代码如下:#include<iostream>using namespace std;int a[101];int main(){ int n,m,sum=0,average=0,b; while(cin >>n>>m) { if(n%m==0) { for(int i=1;i<=n;i++) { a[i]=2*i; sum+=a[i]; average=sum/m; if(i%m==0) { if(i!=n) { cout <<average<<' '; sum=0;average=0;} else { cout <<average<<endl;sum=0;average=0;} }}} else { b=n/m; for(int i=1;i<=b*m;i++) { a[i]=2*i; sum+=a[i]; average=sum/m; if(i%m==0) { cout <<average<<' '; sum=0;average=0; }} for(int i=b*m+1;i<=n;i++) { a[i]=2*i; sum+=a[i]; average=sum/(n-b*m); if(i==n) { cout <<average<<endl; sum=0;average=0;} }}}}
代码虽然比较长,但是却能提供一个清晰的思路,个人认为还是值得的。对于不能整除的部分,我们通过求余数的方法先拿出来,计算个数,单独进行求平均值。个人感觉好像没什么坑,只是单纯的思路复杂。
T5: 有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?
这是第一道让我难堪的题目,当天毫无思路。母牛生小牛,小牛要长4年,这怎么求呢?最后想到了数组。定义数组a[4],我们将母牛放在a[0],刚出生的小牛放在a[3],每过一年,a[3]=a[0],a[2]=a[3],a[1]=a[2],a[0]=a[0]+a[1]。也就是说,这样做的话,只有在a[0]中的牛才会生小牛,从而解决了问题。代码如下:#include<iostream>#include<cstring>using namespace std;int main(){ int a[5]={0},n,sum=0,t=0; while(cin >>n) { if(n==0)break; a[0]=1; for(int i=1;i<=n;i++) {sum=a[0]+a[1]+a[2]+a[3]; a[0]+=a[1]; t=a[0]; a[1]=a[2]; a[2]=a[3]; a[3]=t; } cout <<sum<<endl;//cout <<a[0]<<a[1]<<a[2]<<a[3]; sum=0; memset(a,0,sizeof(a)); }}
T6:“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。请写一个程序判断读入的字符串是否是“回文”。 说实话,这道题我是不太会的,所幸书上有一道相似的题,然后就照着葫芦画瓢。先看看代码:#include<iostream>#include<cstdio>#include<cstring>using namespace std;int main(){ char s[105]; int len,i,j,n; while(cin >>n) { for(int k=0;k<n;k++) { cin >>s; len=strlen(s); i=0;j=len-1; while(i<=j&&(s[i]==s[j])) { i++;j--; } if(i>j)cout <<"yes"<<endl; else cout <<"no"<<endl; } }}
这里运用了一个函数strlen(),意为“字符串()的长度”,要说注意点儿啥,首尾可以一起判断算是一个吧,这样也可以做到优化时间。
T7: 输入一个十进制数N,将它转换成R进制数输出。
这个题也是不太会,和上一道题一样,看书写呗。书上用了一个自定义函数,用来转换进制,就当是学习一下函数的使用。代码如下:
#include<iostream>#include<cstdlib>using namespace std;void TurnData(int n,int a);char ch[6]={'A','B','C','D','E','F'};int main(){ int n,a; while(cin >>n>>a) TurnData(n,a); return 0;}void TurnData(int n,int a){ int x[17],i,j,k=0; if(n<0)cout <<'-'; j=abs(n); do { k++; i=j%a; j/=a; x[k]=i; }while(j!=0); for(int h=k;h>=1;--h) { if(x[h]<10)cout <<x[h]; else cout <<ch[x[h]-10]; } cout <<endl;}
书上的原题时输入一个10进制数,分别输出2,8,16进制数,本质时一样的。重点解释一下函数部分,对于a进制,实际上就是逢a进1,因而我们可以用求余来做。求余,取余,原数除以a,如此循环,直至原数等于0。
注意,在我们输出的时候,我们要注意输出的顺序,记住是逆序输出的。而进制高于10时,就需要ABCDEF的帮助。
T8:求A^B的最后三位数表示的整数。说明:A^B的含义是“A的B次方”。
这个题本身不难,A的B次方,一个for语句就解决了。只是当A、B取值较大时,不难想象,这是一个天文数字,会非常的大。我们找不到一个存储类型来储存这个数,这就是关键。因而,此题可以巧妙转换。我们知道,要知道一个幂的后三位,只需要对这个数一直%1000即可,比如1230%1000=230;2323456789%1000=789......这样我们这个问题就简单了,只要每次乘以一个数对1000取余即可。代码如下:
#include<iostream>using namespace std;int main(){ int m,n,r; while(cin>>m>>n&&m>0&&n>0) { r=1; m%=1000; for(int i=1;i<=n;i++) r=r*m%1000; cout<<r<<endl; }}
T9:有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线。
这道题就用到了递推的方法,1→2只有1种方法,1→3有2种,1→4有3种,1→5有5种,到这里我们大概可以猜到了,从第三个开始,每一个的数量等于前两个的数量和,从而找到了递推公式:a[n]=a[n-1]+a[n-2],题目就顺利解决了。不过,这里有一个坑,当蜜蜂从1→50时,如果我们用int定义,我们就会发现,结果是负数,也就是说,int 对于这道题不够用了,因而我们选用long long int进行定义。具体代码如下: #include<iostream>using namespace std;int main(){ long long int a[50]={0}; int m,n,b; cin >>b; for(int i=1;i<=b;i++) { cin >>m>>n; a[1]=1; a[2]=2; a[3]=3; a[4]=5; for(int j=5;j<=n-m;j++) { a[j]=a[j-1]+a[j-2]; } cout <<a[n-m]<<endl;}}
另外,除了这道题外,上楼梯的解题思路同样是运用递推,因而不再展开介绍,请读者自行思考。
T10:有如下方程:Ai = (Ai-1 + Ai+1)/2 - Ci (i = 1, 2, 3, .... n).若给出A0, An+1, 和 C1, C2, .....Cn.请编程计算A1 = ?
这道题猛地一看好像和上一题一个样,都是递推,而且还将递推公式告诉我们了,那这道题不是很easy吗?然后就照着这个公式写,呵呵,wrong answer.其实,这道题并没有我们想象的那么简单,这个递推公式并不是我们所要的,也算得上是一个坑吧,真正的递推公式需要我们自己去推: n=1时,2a1=a0+a2-c1 n=2时,3a1=2a0+a3-4c1-2c2n=3时,4a1=3a0+a4-6c1-4c2-2c3……n=k时,(k+1)a1=ka0+ak-2kc1-(2k-1)c2-……-2ck这才是真正的公式。我们将这个公式带进去就对了,代码如下:
#include <iostream>#include <cstdio>using namespace std;int main(){ int n; double A0, An,A1,k; double c[3005]; while(cin >>n) { cin >>A0>>An; for(int i = 1; i <= n; i++) cin >>c[i]; A1 = n / (n + 1.0) * A0 + 1.0 / (n + 1) * An; k = n * 2.0; for(int j = 1; j <= n; j++) { A1 -= k / (n + 1) * c[j]; k -= 2.0; } printf("%.2lf\n", A1); } return 0;}
T11:参加过上个月月赛的同学一定还记得其中的一个最简单的题目,就是{A}+{B},那个题目求的是两个集合的并集,今天我们这个A-B求的是两个集合的差,就是做集合的减法运算。
首先我们要先读懂题目,A-B是什么意思。多读几遍会明白的。其实,A集合是主体,我们需要判断对于A集合中的每个元素,是否能在B集合中找到一个相同的数与之对应,如果有,我们在输出时就不用再输出这个数了,这便实现了A-B,而当A集合中所有的元素在B中都有一个与之对应的话,这时便是空集,我们输出“NULL”,结束。代码如下:#include<algorithm>#include<cstdio>using namespace std;int main() { int a[100],b[100],C[100]; int i,j,k; int n,m; while(cin >>n>>m) { int d[100]={0},num=0; if(m==n&&m==0)return 0; else { for(i=0;i<n;i++)cin >>a[i]; for(i=0;i<m;i++)cin >>b[i]; for(j=0;j<n;j++) { for(k=0;k<m;k++) { if(a[j]==b[k])d[j]= 1; } } for(i=0;i<n;i++) { if(d[i]!=1) { sort(a, a + n); cout <<a[i]<<" "; C[num++]=a[i]; } } if(num==0)cout <<"NULL"; cout <<endl; } } }
T12:在一个平面内有两个点,求两个点分别和原点的连线的夹角的大小。注:夹角的范围[0,180],两个点不会在圆心出现。
这算是一道数学题,我们可以利用余弦定理来解题,个人认为唯一要注意的地方就是如何将余弦值转换成角度值。我们先看一下代码:#include<iostream>#include<cmath>#include<cstdio>using namespace std;const double PI=3.1415926;int main(){ int t; double x1,x2,y1,y2,a,b,c,d,e; cin >>t; for(int i=1;i<=t;i++) { cin >>x1>>y1>>x2>>y2; a=sqrt((x1*x1)+(y1*y1)); b=sqrt((x2*x2)+(y2*y2)); c=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); d=(a*a+b*b-c*c)/(2*a*b); e=acos(d); printf("%.2lf",e*180/PI); cout <<endl; }}
这里我们运用了一个数学函数:acos(),即反三角函数,可以将余弦值转换成弧度,然后我们就好转换成角度了。
T13:一个整数,只知道前几位,不知道末二位,被另一个整数除尽了,那么该数的末二位该是什么呢?
这个题并不难,只是有一个坑,如果用一般的方法,我们会发现,如果这个数的后两位都是0的话,结果只会输出“0”而不是两个0。为了避免这个问题,我用分类的方法,既然它少一个0,那我自己加一个不就好了。所以让我们来看一下代码:
#include<iostream>using namespace std;int main(){ int a,n,b,e; while(cin >>a>>b) { e=1; if(a==0&&b==0) break; for(int i=0;i<=9;i++) for(int j=0;j<=9;j++) { n=100*a+10*i+j; if(n%b==0) { if(i!=0) {if(e==1) cout <<10*i+j; else cout <<' '<<10*i+j; e++; } else {if(e==1) cout <<'0'<<j; else cout <<' '<<'0'<<j; e++; } }}cout <<endl; }}
T14:把一个偶数拆成两个不同素数的和,有几种拆法呢?
这个题我们可以分两步去做:第一步,我们可以先求出比这个偶数小的所有素数。第二步,用循环来判断是否能拆成两个素数,每有一种,数量加一。具体代码如下:#include<iostream>#include<cmath>using namespace std;int c[1000]={0};int main(){ int n,a,b=0,k=1; while(cin >>n) { if(n==0)break; b=0;k=1; for(int i=2;i<n;i++) { a=0; for(int j=2;j<=sqrt(i);j++) { if(i%j==0)a++; } if(a==0){c[k]=i;k++;} } for(int i=1;i<k;i++) { for(int j=i+1;j<=k;j++) if(c[i]+c[j]==n)b++; } cout <<b<<endl; }}
T15:有二个整数,它们加起来等于某个整数,乘起来又等于另一个整数,它们到底是真还是假,也就是这种整数到底存不存在,实在有点吃不准,你能快速回答吗?看来只能通过编程。例如:x + y = 9,x * y = 15 ? 找不到这样的整数x和y。1+4=5,1*4=4,所以,加起来等于5,乘起来等于4的二个整数为1和4,7+(-8)=-1,7*(-8)=-56,所以,加起来等于-1,乘起来等于-56的二个整数为7和-8。
这道题也算是一道数学题,考察我们对公式法的运用情况。即:x=[-b+(-)sqrt(b*b-4ac)]/2a 代码如下:#include<iostream>#include<cmath>#include<cstdio>using namespace std;int main(){ int n,m; double a,b; while(cin >>n>>m) { if(n==0&&m==0)break; a=1.0*(n-sqrt(n*n-4*m))/2; b=1.0*(n+sqrt(n*n-4*m))/2; if(a==(int)a&&b==(int)b)cout <<"Yes"<<endl; else cout <<"No"<<endl; }}
我们如何判断结果是不是整数呢?首先,我们将其定义为double,求出结果后将其和用int进行强制转换后的数据进行比较,如果二者相等,那么这个数就是整数。
Summary:
写到这里,说明已经接近了尾声,通过这将近一周的c++编程,自己确实收获了许多,也成长了许多。老实说,有一些题的做法,之前并没有使用过或者说学习过,但是没办法,不会就要抓紧时间将它弄懂,比如:递推、进制转换……我曾怀疑过这些题目的意义,有些都没有学过如何去做,为什么还要我们去做,有什么意义吗?现在我想我明白了,这些题不仅仅只是考察一个人的编程能力,同时还考验一个人的学习能力,在有限的时间内,能否自主学习新知识来解决一系列问题。虽说自己并没有将所有的题目全部完成,算是一点遗憾,不过已经满足了,正如老师所说,做这些题目的意义,并不是为了答题而答题,而是为了学会一门知识。授之以鱼不如授之以渔,就是这个道理。