航类C语言解谜赛《苍穹一粟》题解
《苍穹一粟》题解
许文冲(Arthas)
目录
题目目标
找到四块碎片,拼成完整的题面,并完成一道简单的编程题。
碎片1
一阶段:线索
碎片1的线索在《苍穹一粟》中被直接给出:
古者包牺氏之王天下也,仰则观象于天,俯则观法于地;观鸟兽之文与地之宜;近取诸身,远取诸物,于是始作八卦,以通神明之德,以类万物之情。
这段文字形容的是八卦,指向E4中《千年后的回响》
二阶段:谜面
从《千年后的回响》中的八卦图案点入超链接,便可进入谜面
三阶段:八卦迷阵
规则
利用四个按钮对八卦盘面进行移动,使其与中间的八卦图形对应。(应该很容易看出)
4个按钮的作用(从左到右):
- 上层(外圈)逆时针旋转
- 中层(中圈)顺时针旋转
- 下层(内圈)逆时针旋转
- 乾卦(正上方卦象)三个位置对调。
解法
这个题不是很难,基本上一圈一圈逐步调整就可以。
一种解法:
44114144322224433333222412233333
碎片2
一阶段:线索
碎片2的线索在E5《繁星若尘》中的超链接“银河帝国星图”中:
写秋痕,采秋,则更用暗中之明,明中之暗……草蛇灰线,马迹蛛丝,隐于不言,细入无间。
很明显,指向《草蛇灰线》,同样在E5中
二阶段:谜面
从《草蛇灰线》中的超链接进入谜面。
三阶段:数字魔盘
规则
谜题是一个数字华容道模型,但是只提供了给你空位以及四周的视角。(拿到谜题后到处移动那个空位,应该不难发现是一个\(4\times 4\)的方格,其中的数字是\(1-15\)),完整的盘面如下:
4个按钮的作用(从左到右):
- 空格左移一格,即左侧数字移到空格
- 空格上移一格,即上方数字移到空格
- 空格下移一格,即下方数字移到空格
- 空格右移一格,即右侧数字移到空格
解法
这个东西在网上随便搜一搜就有通解了,比魔方要简单得多,而且还挺好玩。
下面直接提供一种现成解法:
LLLUURRRULLLDRRULLDRULDRRDDLUUURDDLUURDLURRDDLLURRDLUURDDDLLLURRRDLLURRDLURDLUUURDDLUURDLLLDDRUULDRRURDLLURDDLLURRRDLLURRDLURDLUURDDLLLURRRDLLLURRRDLURDLLLURRDLLURRDLURRDLULDRRULLDRULDRURDLLULDRRR
碎片3
一阶段:线索
碎片3的线索在E6《星空暗流》中的超链接“星空暗流”中:
斗指东南维,为立夏,万物至此皆长大,故名立夏也。
指向的是E4中《\(Pure\ Brightness\)》一题。
二阶段:谜面
此题中列出了24节气的中英文对照表,其中在立夏的中文处埋藏了超链接,点击后进入谜面。
三阶段:迷失丛林
规则
本题很容易能看出来是一个迷宫,但每次只能看到一格,且你不知道迷宫多大,一开始处于哪一格,最后要走到哪一格。
4个按钮的作用(从左到右):
- 向左走
- 向上走
- 向下走
- 向右走
解法
还能有什么解法,慢慢试呗!就是要花亿点点时间,下面直接亮迷宫全貌:
碎片4
一阶段:线索
本题无第一阶段
二阶段:谜面
点击《苍穹一粟》题目配图的最下方区域(实际上这里是两张图拼起来,只是显示的时候看起来像一张图)直接进入谜面。
三阶段:万全决策
规则
此题的规则是最难懂的。
首先,正如题目所说,不要寄希望于只靠自己解开它!——因为每个人只能拿到一部分的题面。此题的题面一共有四组,是根据你的ip地址最后几位除以4的余数来分配的。因此,你在进入谜题页面时虽然只能看到两个数字,但实际上有8个!(也就是一一对应下面的8个圆形按钮)。也就是说,这道题想得到完整的题面,需要好几个人的协助(至少能覆盖ip地址被4除的4种余数),接着,你还需要确定这四组题面分别对于下面的哪两个按钮(设计的比较友好,就是(1,5)(2,6)(3,7)(4,8))
接下来,确定题面之后,就要弄懂这个谜题的规则。一开始直接连续空过,可以发现,两个球的数字在不断的减小,直到显示你已失败(例如,第一个数字为:62,61,59;第二个数字为:14,12,9)在这里,其实就能观察出,本题是一个自由落体模型,每个数字对于一个在做自由落体运动的物体,且拥有自己的初位置与初速度,并具有向下的加速度1。所以,第二步需要确定的是8个物体的初位置与初速度
那么,下面八个按钮的作用是什么呢?其实8个按钮分别对于将这8个物体向上抛,使其速度变为+4,并消耗1点能量。而谜题的目标便是,利用16点能量,坚持20回合,使8个物体均不落地。
空过不会抛任何物体,也不会消耗能量。
解法
8个物体的初位置与初速度如下(速度向上为正)。
[62, 14, 40, 97, 22, 34, 15, 49]
[-1, -2, -3, -32, -1, -2, -2, -4]
一种抛球方案为(0为空过,答案不唯一):
2 7 4 5 8 6 3 1 2 7
4 5 8 6 3 2 0 0 0 0
完整题面
居然还真有人能看到这里来(bushi)
恭喜你找到了地图!
这张地图由\(m\times n\)个数字构成,如图所示(该示例中\(m=6,n=7\)):
以下是地图破解方法的第一步:
地图中的坐标表示法为:左下角为\((0,0)\),向右为\(x\)轴(长度为\(n\)),向上为\(y\)轴(长度为\(m\))。
在这\(m\times n\)个数字中,有且仅有7个质数。且这7个质数中,有且仅有两个点横坐标相同,有且仅有另外两个点纵坐标相同,这两组点分别连接成横线和竖线,找到这两条线的交汇处的点,该点上的数字与其横,纵坐标之和记为\(k\)。(此处20+2+4=26)
以下是地图破解方法的第二步:
将整个地图中的数字对第一步中得到的\((k+纵坐标-k的最小非1因数)\)进行取余,得到新的\(m\times n\)表。
以下是地图破解方法的第三步:
将第二步中得到的\(m\times n\)表,每一列从上往下,依次用较大的数减去较小的数再乘2,得到新数,直到每行得到一个数,规则示例如下(例如第二步图中第一列):
(16 - 12) * 2 = 8
(9 - 8) * 2 = 2
(18 - 2) * 2 = 32
(32 - 3) * 2 = 58
(58 - 23) * 2 = 70
因此第一列得到5
从而得到一个长度为\(n\)的一维序列:
以下是地图破解方法的第四步:
将第三步中得到的一维数字序列中的所有数取异或,得到\(a\),例如70^16^8^18^50^44^30=76
。将\(a\)分别\(n,m\)取余,得到的数在原地图中的坐标,与第一步中数所在位置中点的数即为秘密文件的坐标。(位置向上,向左取整)
在示例中,原地图坐标为\((6,4)\)的数为与第一步质数中间为35。
输入(与第一部分同时出现)
第一行输入两个正整数\(m,n\),用空格隔开,分别表示地图的行数和列数。
接下来,\(m\)行,每行\(n\)个用空格隔开的正整数,表示每一个坐标点中的数字\(a_{mn}\)(输入顺序为从上往下,从左往后)。
输出(与第四部分同时出现)
输出一个数,为秘密文件所在坐标点中的数字。
输入样例(与第一部分同时出现)
6 7
12 33 13 46 51 14 34
44 37 20 27 35 67 10
90 14 36 47 42 12 85
18 22 43 38 21 27 45
78 99 10 18 29 50 14
23 40 28 51 75 22 15
输出样例(与第四部分同时出现)
35
数据范围(与第一部分同时出现)
- \(5<m,n<50\)
- \(a_{mn}\)在
int
范围内
题解
题目分析
真没什么难的,基本就是一些代码基础的东西,单论代码难度比前几次的上机和练习赛里任何一道压轴题都简单。
示例代码
//
// Created by moc85 on 2022/4/27.
//
#include <stdio.h>
int map0[50][50];
int map1[50][50];
int map2[50];
int prime[50][50];
int isPrime(int x);
int smallestFactor(int x);
int Step1(int m, int n);
void Step2(int m, int n, int s);
void Step3(int m, int n);
int Step4(int m, int n);
int px = 0, py = 0;
int main()
{
int m = 0, n = 0;
scanf("%d%d", &m, &n);
for (int i = m - 1; i >= 0; i--) {
for (int j = 0; j < n; j++) {
scanf("%d", &map0[i][j]);
}
}
int seed = Step1(m, n);
Step2(m, n, seed);
Step3(m, n);
int result = Step4(m, n);
printf("%d", result);
return 0;
}
int isPrime(int x)
{
if (x == 1) {
return 0;
} else if (x == 2) {
return 1;
}
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) {
return 0;
}
}
return 1;
}
int smallestFactor(int x)
{
for (int i = 2; i <= x; i++) {
if (x % i == 0) {
return i;
}
}
return x;
}
int Step1(int m, int n)
{
for (int i = 0; i < m; i++) {
int primeNum = 0;
for (int j = 0; j < n; j++) {
if (isPrime(map0[i][j])) {
primeNum++;
}
}
if (primeNum == 2) {
py = i;
break;
}
}
for (int j = 0; j < n; j++) {
int primeNum = 0;
for (int i = 0; i < m; i++) {
if (isPrime(map0[i][j])) {
primeNum++;
}
}
if (primeNum == 2) {
px = j;
break;
}
}
return map0[py][px] + px + py;
}
void Step2(int m, int n, int s)
{
for (int j = 0; j < n; j++) {
for (int i = 0; i < m; i++) {
map1[i][j] = map0[i][j] % (s + i - smallestFactor(s));
}
}
return;
}
void Step3(int m, int n)
{
for (int j = 0; j < n; j++) {
int tmp = 0;
for (int i = m - 1; i >= 0; i--) {
if (map1[i][j] >= tmp) {
tmp = map1[i][j] - tmp;
} else {
tmp = tmp - map1[i][j];
}
if (i != m - 1) {
tmp *= 2;
}
}
map2[j] = tmp;
}
return;
}
int Step4(int m, int n)
{
int tmp = 0;
for (int i = 0; i < n; i++) {
tmp = tmp ^ map2[i];
}
return map0[(tmp % m + py + 1) / 2][(tmp % n + px) / 2];
}
写在最后
这个题算是我整的一个活,由于整个出题,谜面设计,线索布置全是我一个人完成的,因此很多地方还设计的比较粗糙(比如那几个不太好看的谜面网页)
出这道题的本意在于想让大家在繁忙的学习之余能玩点不一样的东西,给大家一种“击败团本BOSS”的感觉,并且激发大家的想象力和创造力,算是给大家的学习生活“加点料”。
除此之外,我在这道题里还传达了一种我个人的理念,那就是鼓励大家团队协作解决问题,而不是自己一个人闷头干。经过我在出题时的脑测,我认为只凭一个人独立解出前三个题是有相当难度的,更别说单人直接不可解的第四题。因此,我想看一看,在只有一份首杀奖品的情况下,大家的团队协作能力到底能被激发到哪一步(其实还是没钱)。
大家现在还是大一的学生,但或许已经听说了“内卷”这个词,当你们到了大二,大三,开始一步步面对升学的压力时,这种“内卷”的风气会更加明显。在这种风气下,会有一批学生开始不愿意将自己的知识,方法,心得分享给他人,而是自己闷头干,不希望因为将自己的所得传授给竞争对手从而使自己落得下风,这样就会造成一种恶性循环。
但实际上,不论是学习,还是工作,生活,你会遇到很多问题,靠这种“消极个人英雄主义”是行不通的,只有大家暂时放下个人的得失,想着“我要解决这个问题”而不是“我要战胜他们”,才能攻克。你要相信,上天不会亏待愿意分享的人,你所帮助别人的,一定也会在一个意想不到的时间,回到你的手中~~
最后,祝大家在期末考试中能取得好的成绩。浩渺行无极,扬帆但信风!