用C语言解决“逢7击掌”问题
原题目
-
这是一道PTA题目,原题是这样的:
一群人围坐成一圈玩报数的游戏。
游戏规则是:从1开始报数,若遇到7的倍数或者个位为7的数则击掌代替,若犯错则要受罚。
可是这些人都太聪明了,竟然没人犯错!
请问同样聪明的你,现在当报数到 n 时,击掌共次数 c 是多少?
输入格式:
n
输出格式:
c
输入样例:
21
输出样例:
4
第一次尝试
-
一开始,我觉得最先能想到办法是利用循环:
当数字是7的倍数或者个位数为7,那么计数器+1
7的倍数,C语言的表达式为:
i%7 == 0
个位数为7,C语言的表达式为:
i%10 == 7
这是最为清晰的一个思路,非常容易理解
相应完整代码为
#include <stdio.h> int main() { int n, c = 0; //n是待输入数据,c是计数器 scanf("%d", &n); for (int i = 1; i <= n; i++) { //如果符合条件,计数器+1 if ( i%7 == 0 || i%10 == 7) c++; } printf("%d\n",c); return 0; }
(语法标准采用C99)
-
用GCC编译器运行上面的代码:
那么显然,我们认为在GCC上通过了
-
提交系统后?
系统提示超时,说明我们得优化思路!
优化超时问题
-
这里我们观察规律:
每70个数里会有16个数击掌:
1-70
7的倍数:
14、21、28、35、42、49、56、63、70个位数为7:
17、27、37、47、57、67个位数为7且为7的倍数:
7共16个
71-140
7的倍数:
84、91、98、105、112、119、126、133、140个位数为7:
87、97、107、117、127、137个位数为7且为7的倍数:
77共16个
……
所以办法是:
- 先计算有多少个70个为一组的组数(×16,得到前面的击掌数)
- 再计算剩余的不满70个数的那一组的击掌数
修改后的代码:
#include <stdio.h> int main() { int n, c = 0, cnt; //n是待输入数据,c是后几个数的计数器,cnt计算有多少个70个为一组的组数 scanf("%d", &n); /* 寻找规律:每70个数会有16个数击掌,只要判断剩下的 */ cnt = n / 70; //计算有多少个70个为一组的组数 n %= 70; //将n变为最后剩余不满70个数的一组 for (int i = 1; i <= n; i++) { //如果符合条件,后几个数的计数器+1 if ( i%7 == 0 || i%10 == 7) c++; } c = c + cnt * 16; //计算总共的击掌数 printf("%d\n",c); return 0; }
(语法标准采用C99)
-
提交系统后
终于不超时了!
算法优化
-
实际上,本题不使用循环也能完成:
我们只需观察最后一个数n本身,利用算出1~n中7的倍数,个位数为7的数,最后计数相加
请看代码:
#include <stdio.h> int main() { int n; //待输入数据 int c = 0; //计数器 scanf("%d",&n); c += n / 7; //是7的倍数 c += (n - 7) / 10 + 1; //个位数为7 c -= (n - 7) / 70 + 1; //个位数为7且为7的倍数(从7开始,每70个一个),这些数重复计数了,需要减去 printf("%d\n", c); return 0; }
以上代码是如何实现的,请读者细品!
总结反思
用C语言解决问题,要从一开始想到的算法去一步一步优化。
比如本题中一开始的循环虽然可以解决问题,但是循环次数过多,能否减少循环次数呢,加快运行速度呢?
有时候也要换一个角度思考问题,比如本题最后一个方法就没有采用循环,事实上,更加加快运行速度。
C语言的特点是语法简单,程序执行效率高,适合用于嵌入式/底层开发,这就要求我们一定要注意算法的优化,加快运行效率!