注:此非正常方法仅供娱乐,请勿用于比赛或需要他人 Review 的场合。
考完试了,是时候来放 (gao) 松 (dian) 一 (shi) 下 (qing) 了
进入学校的洛谷奥赛团,发现布置了一道新题目:P1996 约瑟夫问题
个人()围成一圈,从第一个人开始报数,数到 的人出列,再由下一个人重新从 开始报数,数到 的人再出圈…… 以此类推,直到所有的人都出圈,请输出依次出圈人的编号。
要求用链表做,但我也可以搞点事情嘛~
十分钟后敲出一份 AC 代码:
#include<cstdio> | |
#include<cstring> | |
int n, m, i, count, jump; | |
bool a[100]; | |
int main() | |
scanf("%d%d", &n, &m); | |
memset(a, false, sizeof(a)); | |
count = 0; | |
i = -1; | |
jump = 0; | |
while (count < n){ | |
i++; | |
i %= n; | |
if (!a[i]) | |
jump++; | |
if (jump == m){ | |
printf("%d ", i+1); | |
a[i] = true; | |
count++; | |
jump = 0; | |
} | |
} | |
return 0; | |
} |
不断地循环,用一个 bool
数组标记出列。
AC 是 AC 了,但是…… 中规中矩,不符合我们搞事情的风格呀
我们来稍微简化一下 <_<
简化亿下
首先,最关键的就是中间这段 while 循环了,所以我们先从这里入手。
# Step 1
i++; | |
i %= n; |
开始先是指针 i
累加,然后用一个求余处理溢出。
合并一下,变成:
++i %= n; |
# Step 2
if (!a[i]) | |
jump++; |
这里需要动点脑筋,我们知道 a[i]
是 bool
型数组,所以就有 True
和 False
两种情况,作加法运算时转成 int
就是 1
和 0
。我们只需要让 jump
累加上 a[i]
的取反值就好了。
jump += !a[i]; |
# Step 3
现在 Step 1 和 Step 2 的 4 行代码已经缩成了 2 行。
++i %= n; | |
jump += !a[i]; |
但是这还不够,我们可以再合并一下:
jump += !a[++i %= n]; |
# Step 4
if (jump == m){ | |
printf("%d ", i+1); | |
a[i] = true; | |
count++; | |
jump = 0; | |
} |
接下来是一段 if 逻辑,我们这里需要用三元操作符来代替。
条件 ? 条件为 True 时执行的代码 : 条件为 False 时执行的代码 |
我们把三元操作符塞进 printf
函数里面:
printf(jump == m ? "%d" : "" , i+1); |
但是剩下的三行赋值怎么办?
这里我们就需要用到一个很巧妙的工具了:与运算符 &&
我们都知道,与运算符正常的打开方式是这样子的:
条件 1 && 条件 2 |
只有两个条件全为 True
,整个表达式才为 True
。如果条件 1 为 True
,则不会判断条件 2。
我们稍微思考一下,** 如果条件 2 恒为 True
,那么整个表达式的值完全取决于条件 1。** 而 &&
运算符两边是可以塞下一个赋值表达式的!
这样,我们就可以把剩下的三条赋值表达式全部用连续的 &&
运算符塞进一个括号里面去!
赋值表达式本身的值等于最终被赋值的值,我们只需要通过取反运算符 !
或者直接 +1
让表达式的值恒为 True
就好了。
所以就变成了这样:
(jump += !a[++i %= n]) == m && (a[i] = true) && (count++)+1 && !(jump = 0) |
与上面的 printf
函数合并一下,变成:
printf((jump += !a[++i %= n]) == m && (a[i] = true) && (count++)+1 && !(jump = 0) ? "%d " : "", i+1); |
Perfect!
# Step 5
最后我们只需要将代码整理一下即可。
貌似改动的时候出了点小 bug, i
不能初始化为 -1
,要初始化为 0
。我也不知道是什么原因。但也好,这就更简单了。
所有的变量和数组都定义为全局变量,直接默认初始化为 0
,这样把初始化的赋值和 memset
都省了。
整理一下,大功告成:
#include<cstdio> | |
int n, m, i, count, jump; | |
bool a[100]; | |
int main(){ | |
scanf("%d%d", &n, &m); | |
while (count < n) printf((jump += !a[++i %= n]) == m && (a[i] = true) && (count++)+1 && !(jump = 0) ? "%d " : "", i+1); | |
return 0; | |
} |
# 尾声
还是没有大佬们厉害。
题目背景
约瑟夫是一个无聊的人!!
我也像是个无聊的人 <_<