算法竞赛入门经典——第3章答案 输入一个由小写字母组成的英文单词,输出用手机的默认英文输入法的键盘序列。例如打出pig这个单词 需按1次p,3次i,(稍作停顿后)1次,记为p1i3i1.
习题3-1 分数统计(stat)
输入一些学生的分数,哪个分数出现的次数最多?如果有多个并列,从小到大输出。
任务1:分数均不超过100的非负整数
任务2:分数均不超过100的非负实数,但最多保留两位小数。
这个类似单词统计词频,按字典序输出频率最高的那些。
【思路】
pic
任务1和任务2差不多,换成double类型即可;现在以任务1为例解答——
一种思路是从第二个读入的数字开始,从已有的集合(数组)中寻找,若重复则该元素的频率变量自增;否则添加新元素;
最后根据频率变量排序,然后截取频率最高的那段(多个或者1个),再对该段按分数变量排序,最后输出这段数组的分数;
代码:
#include<stdio.h> #include<string.h> int main() { int i,j,n,max,s[101],a[100010];//s[]的容量必须比所需大1 存放 while(~scanf("%d",&n)) { memset(s,0,sizeof(s));//需头文件 string.h for(i=1;i<=n;i++) scanf("%d",&a[i-1]); for(i=0;i<n;i++) s[a[i]]++; max=0; for(j=1;j<=100;j++) { if(s[j]>max) max=s[j]; } for(j=0;j<=100;j++) { if(s[j]==max) printf("%d ",j); } printf(" "); } return 0; }
习题3-2 单词的长度(word)
输入若干个单词,输出它们的平均长度。单词只包含大写字母和小写字母,用一个或多个空格隔开。
代码:
#include<stdio.h> #include<string.h> int main() { int n,l,i,sum; double m; char str[100010]; while(~scanf("%d",&n)) { m=0; sum=0; for(i=0;i<n;i++) { scanf("%s",str); l=strlen(str); sum+=l; } m=(double)sum/n; printf("%.2lf ",m); } return 0; }
习题3-3 乘积的末3位
输入若干个整数(可以是正数、负数或者零),输出它们的乘积的末3位。这些整数中会混入一些由大写字母组成的字符串,你的程序应该忽略它们。提示:试试看,在执行scanf("%d")时输入一个字符串会怎样?
输入:AB123CC BB123123321321 DDD22 888888888888888888888888888ZZ -411B
输出:968
输入:AA-11BBB D2CCC
输出:-22
假定末3位是指,不足3位就输出数字本身,如果是负数则包括负号,比如结果是-12,则输出-12;结果是11,则输出11,结果是0,则输出0;
代码:
#include<stdio.h> int main() { int a[50]={0}; int t,c,b,i,sum; sum=1; while(1) { c=scanf("%d",&b); if(c==0) { t=getchar(); if(t=='R') break; }//过滤字母,输入大写字母R结束输入 else if(b!=0) { a[i]=b; i++; }//自动忽略输入的0 c=0; } for(i=0;a[i]!=0;i++) { sum*=a[i]; } if(sum<=999) printf("%d ",sum); else printf("%d ",sum%1000); return 0; }
参考代码:
题目分析: * 题目难度主要在于参差的数据类型输入。 * 题目总思路,使用while语句逐个string作为单词输入。 * 然后通过调用函数bool getInt(...)判断该单词是否整数,另外,再利用该函数的形参将正确的整数返回。 * 将从函数里面得出的整数与结果相乘,乘后保留末三位整数即可。 * 其中,关于如何实现bool getInt(string get, int & nowGet)才是难题。实现方法如下: * 首先,考虑不是整数的情况,1.字符串为空,返回false, 2.是字母或是仅有一个符号。 * 除了字符串为空的情况外,我们可以开始考虑字符串不为空的情况,字符串不为空时,存在三种情况, * 第一种,带符号的整数,第二种,不带符号的整数,第三种,不是整数。 * 关于这三种情况,可以使用一个if-else if- else语句来实现, * 第一种情况:if ('+' == get[0] || '-' == get[0])带上了符号,可以直接忽略读取第[0]位符号位,然后再 * 从最尾位开始读取,读取3位数即可停止,当读取途中不满三位数或者碰上符号位时, * 立即返回。 * 第二种情况:if('0' <= get[0] && '9' >= get[0]) 直接是数字的情况下,跟第一种情况的思路基本一致, * 直接从最尾位开始取个位数,次尾位取十位数,倒数第三位取百位数。 * 第三种情况:由于不符合需要的整数的转换条件,只需要直接返回false即可. * 另外,如果想将字符型的数字转换成数值,只需要将原来的字符型减去'0'即可得到相对应的数值。 **/ #include <cstring> #include <string> #include <cctype> #include <cmath> #include <iostream> using namespace std; bool getInt(string get, int & nowGet) { int stringLong = get.length(); if (0 == stringLong){ return false; } nowGet = 0; if ('+' == get[0] || '-' == get[0]){ // 带符号的正数或负数 if (0 == (stringLong - 1)){ // 它只包含了一个符号,则直接返回 return false; } for (int j = 0, i = stringLong - 1; i > 0 && i >= stringLong - 3; --i, ++j){ if ('+' == get[i] || '-' == get[i]){ // 正在循环转换的过程,这个数字不满三位数,遇上了符号,直接跳出 break; } nowGet += int(get[i] - '0') * (int) (pow(double(10), j)); } return true; } else if('0' <= get[0] && '9' >= get[0]){ // 直接是数字的情况下 for (int j = 0, i = stringLong - 1; i >= 0 && i >= stringLong - 3; --i, ++j){ nowGet += int(get[i] - '0') * (int) (pow(double(10), j)); } return true; } else{ // 是字母 return false; } } int main() { string word; long result = 1; while(cin >> word){ int temp; if (getInt(word, temp)) { result = (result * temp) % 1000; } } cout << result << endl; return 0; }
习题3-4
编程程序读入一行恰好包括一个+或-或*的表达式,输出它的值。运算符保证是二元运算符,且两个运算数均不超过100的非负整数。运算数和运算符可以紧挨也可以有一个或多个空格、TAB隔开。行首尾均可以有空格。提示:选择合适的输入方法可以将问题简化。
代码:
#include<stdio.h> #define N 1000 int main() { char *p=NULL; char s[N]; int x,y; while(1) { fgets(s,sizeof(s),stdin);//it will contain ' ' for(p=s;*p!=' ';p++) { if(*p=='+'||*p=='-'||*p=='*') break; } if(*p!=' ') { switch(*p) { case '+': sscanf(s,"%d + %d",&x,&y);//此处的‘+’两边必须有空格才可以吃掉诸如3 +1这种情况 printf("%d ",x+y); break; case '-': sscanf(s,"%d - %d",&x,&y); printf("%d ",x-y); break; case '*': sscanf(s,"%d * %d",&x,&y); printf("%d ",x*y); break; } } } return 0; }
习题3-5 旋转
输入一个n*n字符矩阵,把它左旋90度后输出
代码:
//3-5旋转 #include<stdio.h> #define N 100 int main() { int i,j,n; char a[N][N]; while(~scanf("%d",&n)) { for(i=0;i<n;i++) { for(j=0;j<n;j++) { scanf("%s",&a[i][j]); } } for(j=n-1;j>=0;j--) { for(i=0;i<n;i++) { printf("%c ",a[i][j]); } printf(" "); } } return 0; }
习题3-6 进制转换1
输出基数b( 2 <= b <= 10)和正整数n(十进制),输出n的b进制表示
思路:关键是除留余数法时,结束的条件可以是当前剩下的数字<=基数或者当前剩下的数字的==它的模
至于逆序输出,可以考虑用递归实现
代码:
#include<stdio.h> int main() { int b,n,a[100],i,t; while(~scanf("%d %d",&n,&b)) { t=0; i=1; while(n>=b)//循环条件不可错了!! { a[i]=n%b; n/=b; i++; } a[i]=n%b; t=i; for(i=t;i>0;i--) printf("%d",a[i]); printf(" "); } return 0; }
//递归 #include<stdio.h> int fac(int n,int b) { if(n>=b) fac(n/b,b); printf("%d",n%b); } int main() { int b,n; scanf("%d %d",&n,&b); fac(n,b); printf(" "); return 0; }
习题3-7 进制转换2
输出基数b( 2 <= b <= 10)和正整数n(b进制),输出n的十进制表示
思路,比如八进制的678,换成十进制就是8*8的0次方+7*8的1次方+6*8的2次方,因此很容易编写
代码:
//3-7 进制转换2 #include<stdio.h> #include<string.h> #define N 100 char s[N]; int main() { int n,i,k,l,t; l=0; scanf("%d %s",&n,s); for(i=strlen(s)-1;i>=0;i--)//此处i值需取到0!!!! { t=s[i]-'0'; k=strlen(s)-1-i; while(k--) t*=n; l+=t; } printf("%d ",l); }
代码:
#include<stdio.h> int main() { char c; int i; while(char c=getchar()) { switch(c) { case 'a': printf("a1"); break; case 'b': printf("a2"); break; case 'c': printf("a3"); break; case 'd': printf("d1"); break; case 'e': printf("d2"); break; case 'f': printf("d3"); break; case 'g': printf("i1"); break; case 'h': printf("i2"); break; case 'i': printf("i3"); break; case 'j': printf("j1"); break; case 'k': printf("j2"); break; case 'l': printf("j3"); break; case 'm': printf("m1"); break; case 'n': printf("n2"); break; case 'o': printf("n3"); break; case 'p': printf("p1"); break; case 'q': printf("p2"); break; case 'r': printf("p3"); break; case 's': printf("p4"); break; case 't': printf("t1"); break; case 'u': printf("t2"); break; case 'v': printf("t3"); break; case 'w': printf("w1"); break; case 'x': printf("w2"); break; case 'y': printf("w3"); break; case 'z': printf("w4"); break; } } printf(" "); return 0; }
注意每个代码细节 少一个等号,少一个符号 等的 以后不可再犯!