算法竞赛入门经典第3章【小结与习题】

习题3-1 分数统计(stat)

输入一些学生的分数,哪个分数出现的次数最多?如果有多个并列,从小到大输出

任务1:分数均不超过100的非负整数

任务2:分数均不超过100的非负实数,但最多保留两位小数。

这个类似单词统计词频,按字典序输出频率最高的那些。


【思路】

pic

任务1和任务2差不多,换成double类型即可;现在以任务1为例解答——

一种思路是从第二个读入的数字开始,从已有的集合(数组)中寻找,若重复则该元素的频率变量自增;否则添加新元素;

最后根据频率变量排序,然后截取频率最高的那段(多个或者1个),再对该段按分数变量排序,最后输出这段数组的分数;

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #define N 10000  
  4.   
  5. typedef struct node {  
  6.     int val;  
  7.     int freq;     
  8. }node;  
  9.   
  10. node a[N];//集合  
  11. int len = 0;//集合大小  
  12.   
  13. int  
  14. isInclude(int key) {  
  15.     int i;  
  16.     for (i = 0; i != len; i++) {  
  17.         if (key == a[i].val) {  
  18.             a[i].freq++;  
  19.             return 1;  
  20.         }  
  21.     }  
  22.     return 0;  
  23. }  
  24.   
  25. int  
  26. cmp1(const void* a, const void *b)  
  27. {  
  28.     return (*(node *)b).freq - (*(node *)a).freq;//descending order  
  29. }  
  30.   
  31. int  
  32. cmp2(const void* a, const void *b)  
  33. {  
  34.     return (*(node *)a).val - (*(node *)b).val;  
  35. }  
  36.   
  37. int cmp1(const void* a, const void *b);  
  38. int cmp2(const void* a, const void *b);  
  39. int isInclude(int key);  
  40.   
  41. int  
  42. main(void)  
  43. {  
  44.     int n, tmp;  
  45.     int i, j;  
  46.       
  47.     scanf("%d", &n);  
  48.     for (i = 0; i != n; i++) {  
  49.         a[i].val = -1;  
  50.         a[i].freq = 1;  
  51.     }  
  52.   
  53.     extern int len;  
  54.     scanf("%d", &(a[len++].val));     
  55.     for (i = 0; i != n-1; i++) {  
  56.         scanf("%d", &tmp);  
  57.         if (!isInclude(tmp)) {  
  58.             a[len++].val = tmp;  
  59.         }  
  60.         //else the element's freq++ @isInclude();  
  61.     }  
  62.       
  63.     qsort(a, len, sizeof (node), cmp1);  
  64.       
  65.     for (i = 0; i != len; i++) {  
  66.         if (a[i+1].freq != a[i].freq) {  
  67.             tmp = i;  
  68.             break;  
  69.         }  
  70.     }  
  71.     qsort(a, tmp+1, sizeof (node), cmp2);  
  72.       
  73.     for (i = 0; i != tmp+1; i++)  
  74.         printf("%d\n", a[i].val);  
  75.       
  76.     return 0;  
  77. }  


习题3-2 单词的长度(word)

输入若干个单词,输出它们的平均长度。单词只包含大写字母和小写字母,用一个或多个空格隔开。

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define N 10000  
  4.   
  5. char str[N];  
  6. char buf[N/100];  
  7.   
  8. int len1(char *s);  
  9. int len2(char *s);  
  10.   
  11. int  
  12. len1(char *s)  
  13. {  
  14.     int count = 0;  
  15.     int words = 0;  
  16.     char *p = s;  
  17.       
  18.     for (; *p != '\n'; p++) {  
  19.         if (*p != ' ')            
  20.             ++count;  
  21.         if (*p != ' ' && (*(p+1) == ' ' || *(p+1) == '\n'))  
  22.             ++words;  
  23.     }     
  24.     return count/words;  
  25. }  
  26.   
  27. int  
  28. len2(char *s)  
  29. {  
  30.     int words = 0;  
  31.     int count = 0;  
  32.     char *p = s;  
  33.       
  34.     while ( 1 == sscanf(p, "%s", buf) ) {  
  35.         p += strlen(buf)+1;  
  36.         count += strlen(buf);     
  37.         ++words;  
  38.     }     
  39.     return count/words;   
  40. }  
  41.   
  42. int  
  43. main(void)  
  44. {  
  45.     fgets(str, sizeof (str), stdin);  
  46.     printf("%d\n", len1(str));//or you can call len2(str);    
  47.     return 0;  
  48. }  
对这个题目做了一个假定。约定输入的总长度不超过10000个字符,每个单词长度不超过100个字符。

(其实也可以只利用一个getchar()写一个就地的算法(不需要额外辅助空间),也就不需要这个假设了)

这样求单词平均长度的方法可以有两个,分别写成函数len1()和len2()



习题3-3 乘积的末3位

输入若干个整数(可以是正数、负数或者零),输出它们的乘积的末3位。这些整数中会混入一些由大写字母组成的字符串,你的程序应该忽略它们。提示:试试看,在执行scanf("%d")时输入一个字符串会怎样?

输入:AB123CC   BB123123321321          DDD22    888888888888888888888888888ZZ      -411B

输出:968

输入:AA-11BBB   D2CCC

输出:-22

假定末3位是指,不足3位就输出数字本身,如果是负数则包括负号,比如结果是-12,则输出-12;结果是11,则输出11,结果是0,则输出0;


分析:

必须假定整数(包括字母)长度小于某个数,下面假定长度不超过1000,整数个数不超过100;

设计算法如下:

每读入一个字符串,保存数字部分的后三位(位数不足则保存数字本身),同时判断符号,是负数则标志变量flag(初始为1)改变符号;

初始product为1,按先后逐个处理数字串, 每次处理过程中,product与当前数字串的数字相乘后再模1000,更新到product;

如果product<100,则输出flag*product,否则直接输出product;

为了确保乘积不会溢出,所以必须每步相乘后都要取模。

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <ctype.h>  
  4. #include <stdlib.h>  
  5. #define N 100000  
  6. #define M 5000  
  7. #define L 5  
  8.   
  9. char input[N];  
  10. char str[M];  
  11. char tmp[L];  
  12. char rev[L];  
  13.   
  14. int  
  15. main(void)  
  16. {  
  17.     int i, len, k;  
  18.     int flag = 1;  
  19.     int product = 1;  
  20.     char *p = NULL;  
  21.   
  22.     fgets(input, sizeof (input), stdin);  
  23.     p = input;  
  24.     while (sscanf(p, "%s", str) == 1) {  
  25.         len = k = 0;  
  26.         for (i = strlen(str)-1; i != -1; i--) {  
  27.             if (len != 3 && 1 == isdigit(str[i]))  
  28.                 tmp[len++] = str[i];  
  29.             if (str[i] == '-') {  
  30.                 flag = -flag;  
  31.                 break;  
  32.             }  
  33.         }         
  34.         tmp[len] = '\0';  
  35.         while (len > 0)  
  36.             rev[k++] = tmp[--len];  
  37.         rev[k] = '\0';    
  38.         product *= atoi(rev);  
  39.         product %= 1000;  
  40.         p += strlen(str)+1;  
  41.         while (*p == ' ') p++;/* 滤空 */            
  42.     }  
  43.     printf("%d\n", product < 100 ? product*flag : product);  
  44.     return 0;  
  45. }  


习题3-4

编程程序读入一行恰好包括一个+或-或*的表达式,输出它的值。运算符保证是二元运算符,且两个运算数均不超过100的非负整数。运算数和运算符可以紧挨也可以有一个或多个空格、TAB隔开。行首尾均可以有空格。提示:选择合适的输入方法可以将问题简化。

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #define N 1000  
  3.   
  4. char str[N];  
  5.   
  6. int  
  7. main(void)  
  8. {  
  9.     char *p = NULL;  
  10.     int op1, op2;  
  11.       
  12.     fgets(str, sizeof(str), stdin);//it will contains '\n';  
  13.       
  14.     for (p = str; *p != '\n'; p++) {  
  15.         if (*p == '+' || *p == '-' || *p == '*')  
  16.             break;  
  17.     }  
  18.     if (*p != '\n'){  
  19.         switch(*p) {  
  20.         case '+':  
  21.             sscanf(str, "%d + %d", &op1, &op2);  
  22.             printf("%d\n", op1+op2);  
  23.             break;  
  24.         case '-':  
  25.             sscanf(str, "%d - %d", &op1, &op2);  
  26.             printf("%d\n", op1-op2);  
  27.             break;  
  28.         case '*':  
  29.             sscanf(str, "%d * %d", &op1, &op2);  
  30.             printf("%d\n", op1*op2);  
  31.             break;  
  32.         }     
  33.     }  
  34.     return 0;  
  35. }  

注意"%d + %d"这个+号两边必须有空格才会吃掉诸如3    +1这种情况。。。



习题3-5 旋转

输入一个n*n字符矩阵,把它左旋90度后输出

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #define N 100  
  3.   
  4. char a[N][N];  
  5.   
  6.   
  7. int  
  8. main(void)  
  9. {  
  10.     int n;  
  11.     int i, j;  
  12.   
  13.     scanf("%d", &n);  
  14.   
  15.     for (i = 0; i != n; i++) {  
  16.         for (j = 0; j != n; j++)  
  17.             scanf("%s", &a[i][j]);        
  18.     }  
  19.       
  20.     for (j = n-1; j != -1; j--) {  
  21.         for (i = 0; i != n; i++)  
  22.             printf("%c ", a[i][j]);  
  23.         printf("\n");  
  24.     }  
  25.       
  26.     return 0;  
  27. }  

注意:a[i][j]

i表示的是列

j表示的是行

比如输出

1 2 3

4 5 6

7 8 9

的第三列,应该是

for (int i = 0; i != n; i++)

printf("%d ", a[i][2]);


习题3-6 进制转换1

输出基数b( 2 <= b <= 10)和正整数n(十进制),输出n的b进制表示

思路:关键是除留余数法时,结束的条件可以是当前剩下的数字<=基数或者当前剩下的数字的==它的模

至于逆序输出,可以考虑用递归实现

递归版本:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2.   
  3. void  
  4. trans(int n, int b)  
  5. {  
  6.     if (n >= b)  
  7.         trans(n/b, b);  
  8.     printf("%d", n%b);  
  9. }  
  10.   
  11. void trans(int n, int b);  
  12.   
  13. int  
  14. main(void)  
  15. {  
  16.     int b, n;  
  17.   
  18.     scanf("%d %d", &b, &n);  
  19.     trans(n, b);  
  20.     printf("\n");  
  21.     return 0;  
  22. }  

非递归版本:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #define N 100  
  3. int a[N];  
  4.   
  5. int  
  6. main(void)  
  7. {  
  8.     int b, n;  
  9.     int k = 0;  
  10.     int i;  
  11.       
  12.     scanf("%d %d", &b, &n);  
  13.     while (n >= b) {  
  14.         a[k++] = n%b;  
  15.         n /= b;  
  16.     }  
  17.     a[k++] = n%b;  
  18.     for (i = k-1; i != -1; i--)  
  19.         printf("%d", a[i]);  
  20.     printf("\n");  
  21.       
  22.     return 0;  
  23. }  



习题3-7 进制转换2

输出基数b( 2 <= b <= 10)和正整数n(b进制),输出n的十进制表示

思路,比如八进制的678,换成十进制就是8*8的0次方+7*8的1次方+6*8的2次方,因此很容易编写

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define N 100  
  4.   
  5. char str[N];  
  6.   
  7. int  
  8. main(void)  
  9. {  
  10.     int n, i, k, tmp;  
  11.     int res = 0;  
  12.   
  13.     scanf("%d %s", &n, str);  
  14.     for (i = strlen(str)-1; i != -1; i--) {  
  15.         tmp = str[i]-'0';  
  16.         k = strlen(str)-1-i;  
  17.         while(k--)  
  18.              tmp *= n;  
  19.         res += tmp;  
  20.     }  
  21.     printf("%d\n", res);  
  22.       
  23.     return 0;  
  24. }  

习题3-8 手机键盘


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值