今天让我来继续挑战题目的权威。
题目
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
解题经历
题目的意思很明确了,(明码中)不能出现选择结构和循环结构(不严谨的说法),也用不了等差数列求和公式(
S
n
=
n
⋅
(
a
1
+
a
n
)
÷
2
S_n = n \cdot (a_1 + a_n) \div 2
Sn=n⋅(a1+an)÷2)。那么,就得想想除了循环,还有什么东西(在流程图中)是循环结构体——函数递归!
于是,初步代码就有了:
# include <iostream>
int n, sum;
int x (int a);
int main (){
scanf("%d", &n);
printf("%d", x(n+1));
return 0;
}
int x (int a){
--a;
x(a);
return sum += a;
}
现在,循环结构体是有了,但是这是一个没有终止的无限循环;我们都知道,在流程图中,想要跳出循环一般都会有一个选择结构,因此就得想办法在不使用if
的情况下,使程序拥有判断能力。
于是,我在脑海中遍历了我所知道的所有语法及知识点,历经2.5h(实际时间也接近1h了)我可算联想到了——短路语句!题目中只说不能使用if
,没说不能使用逻辑表达式啊!
(可能是)所有(编程语言中的)逻辑表达式公有的特性:
- 逻辑与是前后两个表达式都为
true
,最终的结果才为true
;而程序运行时,如果发现第一个表达式为false
,那么它鸟都不鸟第二个表达式,最终结果直接为false
。- 逻辑或是前后两个表达式其中一个为
true
,整个表达式就都为true
;而程序运行时,如果发现第一个表达式为true
,那么它鸟都不鸟第二个表达式,最终结果直接为true
。
因此,只需要把引用函数放在第二个表达式上,当第一个表达式为0
时,就可以跳出循环了!
而这个0
是怎么得到的呢?很简单,我们的变量a
每递归一次,就减一,而正好我们就是要一直累加到0
(累加到1和累加到0是一样的),所以第一个表达式就是a
了。
# include <iostream>
int n, sum;
int x (int a);
int main (){
scanf("%d", &n);
printf("%d", x(n+1));
return 0;
}
int x (int a){
--a;
a && x(a);
return sum += a;
}
最后还是来浅浅的解释一下代码:
第10行就是每递归一次,参数减一(因为第一次引用函数时参数也会减一,所以在第6行参数提前先加一);
第11行前面说了,这个逻辑与是判断的用途:如果a为(递归(或累加)到)0了,那么第二个表达式——递归步骤就被短路掉了;然后就来到第12行开始不断返回并累加数据,最后返回到第6行输出。
总结
最后不得不说这道题考的太灵活了,首先需要摸清题目的本质(流程图步骤),然后再去找可以替代原始语句的方法,最后才是编程运维。
我们以后做题也要学着举一反三,这里不仅仅是数量上的练习,更是方法上的探索,一定要清楚思维的底层逻辑,思考不同语句、语法的异同。你的思维活了,题目考的就死了;你的思维死了,题目考的可就灵活了。
附
花钱学编程浪费钱,还不一定讲的好;
还不如到Noi官网自学,免费回放课,讲的还详细!
连接:https://www.noi.cn/xw/2020-05-13/715542.shtml
纯个人推荐,(与本篇文章无关。)
2024.3.16补充
这样写舒服一些,还更好理解。
#include<cstdio>
int x, d;
int f(int n){
d+=n;
n && f(n-1);
}
int main(){
scanf("%d", &x);
f(x);
printf("%d", d);
return 0;
}
至于为什么int
类型函数没有写返回值,详见这篇文章。