| No.16 求s=a+aa+aaa+aaaa+aa…a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加由键盘控制。
题目分析
在本题中我们需要求一系列叠数相加之和,本题解法很多,我们将使用较为简单直观的 对数字进行处理 的方法,并在最后打印出整个相加求和的流程与结果,由于本题只需要最终的结果,且我的代码中存在对字符串进行处理的部分,为了便于小伙伴们的阅读,我已经在代码中的字符串拼接部分进行注明,如果您并不了解字符串拼接部分的内容,您可以忽略该部分的实现。
回到正题,对于构造叠数的问题,由于本题选择对数字进行处理,我们可以采用以下逻辑实现:
使用一个临时变量tmp存储当前位置的叠数,则下一个叠数 = tmp * 10 + 目标数字
在了解完以上逻辑之后,我们来看代码实现:
#include <stdio.h>
#include <string.h>
int main() {
int num = 0;
int cnt = 0;
int res = 0;
char str[101] = ""; // 用于存储计算过程的字符串
char guo[101] = ""; // 用于临时存储计算过程中的叠数
printf("请输入a和次数,以空格分割\n");
scanf("%d %d", &num, &cnt);
int tmp = num; // 临时变量tmp,记录
for (int i = 0; i < cnt; i++) {
// 添加加号分隔符(第一次循环不加)
if (i > 0) {
strcat(str, "+"); // strcat是C语言中的字符串拼接函数
}
// 使用sprintf对字符串进行构造,
sprintf(guo, "%d", tmp);
strcat(str, guo);
res += tmp;
tmp = tmp * 10 + num;
}
printf("计算过程:\n");
printf("%s ", str);
printf(" = %d\n", res);
printf("最终计算结果是:%d", res);
return 0;
}
| No.17 一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6=1+2+3.编程找出1000以内的所有完数。
题目分析
在本题中,题目规定了一个新的概念 “完数” ,并对其概念做了解释:一个数如果恰好等于它的因子之和,有小伙伴私信说自己碰到很多这种新概念的题目,她说自己很烦恼,因为这类题目的新概念很多,她没办法都背下来
在这里博主要小小的提一下,这类所谓的新概念题目是并不需要我们去将他的概念给背下来的,一般稍微正规一些的题目在题眼中都会将其定义告诉我们,所以小伙伴们不需要为此烦恼,具体问题具体分析,随机应变即可
言归正传,本题其核心之处就在于,需要我们求解每一个数的每一个因数,且根据题中的例子,1应该算是每一个数字的因数,且这个数字本身并不算在因数之内,所以会有 “6=1+2+3” 这个例子,由于题中所给的目标范围为 “1000以内” ,这个范围比较小,我们可以采取最简单粗暴的方法求解某一个数字的所有因数,即:
通过这个方法,我们定义一个额外的变量,用于记录这些因数的和,只要最终其值等于目标数字,则判断该目标数字为“完数”
在了解完以上逻辑之后,我们来看代码,在以下的代码中,我对满足“完数”条件的数字其因数进行打印,以印证代码输出的结果:
#include<stdio.h>
#include<math.h>
void show(int* arr, int n){
// 输出目标数字的因数求和序列
for (int i=0; i<n; i++){
printf("%d ", arr[i]);
if (i != n-1){
printf("+ ");
}
}
printf("= ");
}
bool is_wanshu(int num){
int res = 1;
int arr[1001] = {1}; // 记录因数序列,初始化在完数判断的函数中
int n = 1; // 用于记录因数数组当前存储因数的位置
for (int i=2; i<num; i++){
if(num % i == 0){ // 因数
res += i; // 因数求和
arr[n] = i; // 存储因数
n ++;
}
}
if (res == num){
show(arr, n); // 调用show函数,用于打印因数序列
printf("%d\n", res);
return true;
}
return false;
}
int main(){
int cnt = 0;
for (int i=2; i<=1000; i++){
if (is_wanshu(i)){ // 满足完数条件
printf("完数: %d\n", i);
cnt ++;
}
}
printf("cnt: %d", cnt);
}
|No.18 一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?第10次反弹多高?
题目分析
在本题中,您将会看到一个小球自 100m 高处被放下,经历若干次反弹,本题中需要我们计算 小球第十次落地 时的弹跳总路程以及第十次反弹的高度,具体示意图如下:
在这里我们可以发现, 第一次落地时经过的路程为一开始的100m,每一次落地,小球弹跳的高度为原有的一半,从弹起到落地,每次经过的路程为两段弹起的高度 ,尤其需要注意 从弹起到落地,每次经过的路程为两段弹起的高度 这个点,因为有一些小伙伴在提交本题的代码时由于没考虑到 “下落的路程” 这块的内容,导致提交结果全部爆红的现象
在有了以上分析之后,相信您对于本题的解法也形成了比较清晰的逻辑,我们直接来看代码:
#include <stdio.h>
int main() {
double high = 100.0;
double total = high; // 初始下落100米
for (int i = 1; i < 10; i++) { // 处理第2到第10次落地,共9次循环
high /= 2; // 反弹后的高度
total += 2 * high; // 每次反弹 +下落的路程
}
printf("第十次落地时,共经过:%f m,第十次反弹高度为:%f m\n", total, high / 2);
return 0;
}
| No.19 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。
题目分析
本题为经典的递归入门问题,对于递归如何实现,本处不做详细介绍,这部分内容请小伙伴们自行学习,那么对于本题,我们直接来说一个误区:
有很多小伙伴正向思维,认为:猴子每次吃的是前一天的一半,然后因为嘴馋多吃了一个,因此,在递归逻辑中,其返回的是:
return 2 * sum_peach(day+1) + 1;
提问:这里的正向逻辑哪里不对呢?请小伙伴们思考一下哦
接下来,我们使用正规的数学逻辑来推导一下递归的最终表达式:
我们设当前天和后一天分别为:$x_n 和 x_{n+1}$ ,则有:
$$x_{n+1} = x_n/2 – 1$$
通过一个简单的移项,我们就可以得到:
$$x_n = 2 * ( x_{n+1} + 1)$$
上述公式即是正确的递归计算式,博主在这里顺带提一句,递归最终语句,一般都是依靠严谨的递推式,如果能用数学推导尽量使用推导哦!
在理解上述逻辑推理之后,我们来看看代码,本处代码使用while循环实现,我希望小伙伴们可以动手试试,将循环转换成递归的写法,在大多数情况下,这二者都是可以相互转换的哦:
#include<stdio.h>
int main()
{
int day,x1,x2;
day=9;
x2=1;
while(day>0){
x1=(x2 + 1) * 2;/*第一天的桃子数是第2天桃子数加1后的2倍*/
x2=x1;
printf("第 %d 天的桃子数: %d\n", day, x2);
day--;
}
printf("桃子总数: %d\n",x1);
}
如果您对循环转换成递归有困难,也可以参考以下代码:
#include<stdio.h>
int get_nums(int day){
if (day == 10){ // 第十天只剩下一个桃子,本条件为递归出口
return 1;
}
// int last_day = 2*(get_nums(day+1)) + 1; // 错误推导
int last_day = 2*(get_nums(day+1) + 1);
printf("第%d天的桃子数:%d\n", day, last_day);
return last_day;
}
int main(){
int res = get_nums(1);
printf("共有 %d 个桃子", res);
}
|No.20 两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。
题目分析
本题要求我们根据题中的一些附加条件,为两支队伍中的选手匹配对手,逻辑比较简单,只需要注意:
- 1,对手不能相同
- 2,满足题中的先决条件
要实现上述逻辑比较简单,我们也可以手动推演得到答案,现在我们从一个编程学者的角度打开本题,我们直接采用三个循环实现,为甲队的三位选手依次选择满足条件的乙队选手:
#include <stdio.h>
int main() {
char a_opponent, b_opponent, c_opponent;
// 使用三个for循环遍历所有可能的对手组合
for (a_opponent = 'x'; a_opponent <= 'z'; a_opponent++) {
for (b_opponent = 'x'; b_opponent <= 'z'; b_opponent++) {
for (c_opponent = 'x'; c_opponent <= 'z'; c_opponent++) {
// 检查对手是否互不相同
if (a_opponent != b_opponent &&
a_opponent != c_opponent &&
b_opponent != c_opponent) {
// 应用题目中的约束条件
if (a_opponent != 'x' && // a的对手不是x
c_opponent != 'x' && // c的对手不是x
c_opponent != 'z') { // c的对手不是z
printf("比赛名单为:\n");
printf("a vs %c\n", a_opponent);
printf("b vs %c\n", b_opponent);
printf("c vs %c\n", c_opponent);
}
}
}
}
}
}
那么以上就是本期 C语言从初识到竞赛精通 No.16-No.20 的全部内容了,如果您在阅读本文的过程中有所收获,或者有任何宝贵的建议和想法,欢迎通过邮箱、微信或者留言等方式给我留言交流,您的每一次建议都将是我前进的动力!