"信息与未来"2025年真题解析
点评:2025年信息与未来真题仍符合历年出题风格,并没有考进度过高的算法,而是一如既往地注重思维高度和数学能力。其中前3题相对比较简单,对于平常学习扎实、跟住教练刷题训练的娃来说可以稳稳拿下,基本也就达到了一等奖的水平。T4开始上难度(洛谷评级绿),T5是平常GESP和CSP都不会考到的交互题型,T6对很多考生来说读懂题就已经有些困难了。因此对于备考信息与未来的学生来说,需要从自身实际水平出发,把稳稳拿下的分先拿下,若想冲更高排名,需要对T5、T6花时间系统的强化练习。
B站搜"苏A信奥张教练"有同步视频讲解。张老师作为专职信奥教练多年来指导几十位学生获信息与未来一等奖。2025年两位学生比赛成绩排名头部分别进入南京外国语信奥校队、金陵河西信奥班。若有辅导需要可联系本人VX:QinHuai-shaonian
[1] 幸运数字
题目描述
如果一个 位正整数恰好由数字 组成,Dr. X 就称它为“幸运数字”,例如:
• 是一个幸运数字,因为它是一个四位数,且恰好由数字 组成。 • 不是幸运数字,因为三位幸运数字应该由数字 组成。 • 不是幸运数字,因为我们只考虑不含前导零的正整数。
现在,给定两个正整数 和 ,请你计算 中幸运数字的数量。
输入格式
输入两个空格分隔的正整数 和 。
输出格式
输出一个整数,表示 中幸运数字的数量。
输入输出样例
输入 #1
4 202输出 #1
4输入 #2
1 100000输出 #2
119说明/提示
样例 解释
在 和 之间,幸运数字有 。
数据范围
对于 的数据,满足 。
解题思路
考查枚举和“桶”的思想。寻找 中所有“数字长度为 ,且恰好包含各一次”的整数(类似不含重复数字的排列,且数字集从 到 连续),因此枚举到,使用函数计算当前数字,使用数组标记各个位数字出现的次数,如果到某个数出现的次数不为一定返回,否则返回。
AC代码
#include <bits/stdc++.h>using namespace std;//判断x是否为幸运数 bool check(int x){ int vis[10] = {}, lenx = 0; while(x != 0){ vis[x % 10]++; lenx++; x /= 10; } for(int i = 0; i < lenx; i++){ if(vis[i] != 1) return false; } return true;}int main(){ int a, b, cnt = 0; cin >> a >> b; for(int i = a; i <= b; i++){ if(check(i)) cnt++; } cout << cnt; return 0;}[2] 坐标变换
题目描述
今天我们玩的许多游戏都是真正的 游戏。在计算机能效处理三维对象之前,人们就发明了斜 度视角的 “” 游戏,不需要额外的 加速硬件也能流畅运行。
游戏的地图是一个 行、 列的网格。我们首先将网格里的格子按照从上到下、从左到右的顺序从 开始编号,再把网格顺时针 (向右) 旋转 度,就得到了游戏的地图。此时,网格的行和列发生了变化:旋转后最顶部的格子位于第一行;最左侧的格子位于第一列,如下图所示:

Dr. X 希望实现自己的 游戏引擎,他希望你计算 行 列网格中,编号为 的格子,在旋转 度后的新网格中的行和列。

输入格式
输入空格分隔的整数 和 ,分别表示网格的行数、列数和格子的编号。
输出格式
输出空格分隔的整数 ,表示编号为 的格子在旋转 度后的新网格中的坐标。
输入输出样例
输入 #1
4 3 7输出 #1
3 2输入 #2
16 16 233输出 #2
23 10说明/提示
对于 的数据,。
对于 的数据,。
解题思想
考查数学和空间变换规律。模拟旋转观察找规律,可以发现:①原“/”对角线(为常数)→ 同一行 ②原“\”对角线(为常数)→ 同一列。整理后可发现对于旋转前的任意格子,旋转后其行值 ;列值 , 即 ;
AC代码
#include <bits/stdc++.h>using namespace std;int main(){ int n, m, k; cin >> n >> m >> k; int r = (k + m - 1) / m, c = (k - 1) % m + 1; //r:编号为k的格子原行数 c:原列 int x = r + c - 1, y = n + c - r; //x:旋转后的行 y:旋转后的列 cout << x << " " << y << endl; return 0;}[3] 美味水果
题目描述
Dr. X 收到了一份礼物: 个水果,其中第 个水果的好吃程度为 。新鲜的水果会随时间变得不如最初好吃:
• 每天,Dr. X 可以选择吃掉一个水果,并记录下该天吃掉的水果的好吃程度。 • 没有被吃掉的每个水果,好吃程度将在第二天变为 ,即 “开根号取整”: 是满足 的最大整数。
请计算,在所有可能的吃水果顺序中,Dr. X 最多能获得多少好吃程度的总和。
输入格式
输入包含两行:第一行,一个整数 ,表示水果数量;第二行, 个用空格分隔的整数 ,表示每个水果的初始好吃程度。
输出格式
输出一个整数,表示能够获得的好吃程度总和的最大值。
输入输出样例
输入 #1
2100 10输出 #1
103输入 #2
61 3 7 10 15 21输出 #2
28说明/提示
样例 解释
在第一天,Dr.X 吃掉第一个水果,好吃程度为 ,另一个水果在第二天吃,好吃程度为 ,吃完所有水果,好吃程度的总和为 。
数据范围
对于 的数据,。
对于 的数据,,水果的好吃程度 。
解题思路
考查贪心。贪心策略:每天优先吃掉当前最大好吃程度的水果,其余水果开根号取整,重复 天。因为较大的数若留到后面会因多次开根号而迅速变小,损失更大;而当前较小的数即使留到后面,开根号后变化不大甚至不变(如 ),这样能最大化总和,注意更新剩余水果好吃度时,判断条件至关重要,否则会。
AC代码
#include <bits/stdc++.h>using namespace std;int n, a[100010];long long sum = 0;bool cmp(int x, int y){ return x > y;}int main(){ cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; sort(a+1, a+1+n, cmp); //按好吃度降序排 for(int i = 1; i <= n; i++){ sum += a[i]; for(int j = i+1; j <= n && a[j] != 1; j++){ //注意a[j]!=1的条件,否则TLE a[j] = int(sqrt(a[j])); } } cout << sum; return 0;}[4] 成语接龙
题目描述
小朋友们都很喜欢成语接龙,但如果遇到可以无限接龙的 “防不胜防”、“忍无可忍” 和 “为所欲为”,游戏就会陷入僵局。Dr. X 发明了改进版的成语接龙:把 个汉字写在 行 列的方阵中,要求:
• 第一行(从左往右读)是一个四字成语。 • 最后一列(从上往下读)是一个四字成语。 • 最后一行(从右往左读)是一个四字成语。 • 第一列(从下往上读)是一个四字成语。 • 对角线(从左上到右下读)是一个四字成语。 • 对角线(从右上到左下读)是一个四字成语。 • 上面 个成语各不相同。
下面是一个满足条件的成语接龙:

Dr. X 找到了一本成语词典,你能帮 Dr. X 编程找到词典中所有满足条件的成语接龙方阵吗?
输入格式
输入第一行是整数 ,表示成语词典中成语的数量。
接下来 行,每行四个空格分开的、长度不超过 的字符串(由小写字母和数字组成)。相同的字符串代表同一个汉字。
输出格式
输出一个整数,代表不同成语接龙方阵的数量。对于两个 的方阵,只要存在任意位置的汉字不同,就认为是不同的方阵。
输入输出样例
输入 #1
10hua xiang niao yuyu miao tian xiaxia li ba renren mian tao huahua qian yue xiayu chu jing renji xiang ru yixin xiang shi chengcong ming ling lijian kang cheng zhang输出 #1
1输入 #2
(见下发文件 idiom.txt)输出 #2
58说明/提示
对于 的数据,。
对于 的数据,,且输入中的所有成语均来自真实的成语词典。成语词典中没有重复的成语。
解题思想
考查图论建图、枚举、数学。这道在年是一道“分水岭”题目,洛谷评级绿,很多小有实力的学生是从这道题开始卡住。这道题的策略将成语接龙问题转化为“方阵约束”的计数问题,通过枚举外围四个成语后,再乘法原理统计对角线成语数量。但枚举也有讲究,不能直接枚举所有成语,否则会,而枚举不重复的汉字,大大减少枚举量。通过建图时的邻接表 快速找到候选成语。当外围个成语确定后,验证符合要求的对角线成语,乘法原理只统计数量而不枚举具体组合。
AC代码
#include <bits/stdc++.h>using namespace std;struct Node{ int v, id;};map<string, int> mp; //n较大,成语中有些汉字会重复,mp[i]记录每个汉字的编号int n, cnt, ans; //cnt:不重复的汉字总个数vector<pair<string, string> > zd; //成语字典,存储每个成语的开头汉字和结尾汉字vector<Node> g[10010]; //g[i]存储以编号i开头的成语对应的结尾汉字编号和成语编号 bool vis[10010]; //vis[i]标记编号为i的成语是否使用过int main(){ cin >> n; for(int i = 0; i < n; i++){ string a, b, c, d; cin >> a >> b >> c >> d; //1个成语 zd.push_back({a, d}); if(!mp[a]) mp[a] = ++cnt; if(!mp[d]) mp[d] = ++cnt; //只需要考虑每个成语的开头和结尾即可 } //遍历所有的成语,为每个成语建立关系 for(int i = 0; i < int(zd.size()); i++){ int u = mp[zd[i].first], v = mp[zd[i].second]; g[u].push_back({v, i + 1}); } //枚举左上角的汉字zs,找首字为zs的成语(在g[zs]中) 比枚举n少很多 for(int zs = 1; zs <= cnt; zs++){ for(auto a: g[zs]){ //遍历以汉字zs开头的成语,左->右 if(vis[a.id]) continue; int b = a.v; //右上角汉字编号 vis[a.id] = 1; for(auto c: g[b]){ //遍历以汉字b开头的成语,上->下 if(vis[c.id]) continue; int d = c.v; //右下角汉字编号 vis[c.id] = 1; for(auto e: g[d]){ //右->左 if(vis[e.id]) continue; int f = e.v; //左下角汉字编号 vis[e.id] = 1; for(auto h: g[f]){ //下->上 if(vis[h.id]) continue; int j = h.v; if(j != zs) continue; //j和zs不是同一个汉字continue vis[h.id] = 1; //外围四个成语确定后,统计两条对角线可用的成语数量s1和s2 int s1 = 0, s2 = 0; for(auto x: g[zs]){ if(x.v == d && !vis[x.id]) s1++; } for(auto x: g[b]){ if(x.v == f && !vis[x.id]) s2++; } ans += s1 * s2; //乘法原理,累加每种情况下的s1*s2 vis[h.id] = 0; //恢复h这个成语没使用过 } vis[e.id] = 0; //恢复e这个成语没使用过 } vis[c.id] = 0; //恢复c这个成语没使用过 } vis[a.id] = 0; //恢复a这个成语没使用过 } } cout << ans; return 0;}[5] 迷宫探险
题目描述
Dr. X 把他的机器人超时空传送到了一个未知的空间,也不知道机器人的初始位置和方向。Dr. X 只知道这个未知的空间可以看作由方格组成的矩形方阵,其中的每个方格要么是障碍物,要么是空地。Dr. X 很想知道空间的结构,因此他打算让机器人探索这个空间,并绘制出迷宫的地图。
Dr. X 每次可以给他的机器人发送一条指令,并等待机器人反馈。指令有三种:
• ,向左旋转 度。机器人会返回旋转后的方位( 分别表示东、西、南、北)。 • ,向右旋转 度。机器人会返回旋转后的方位( 分别表示东、西、 南、北)。 • ,尝试向前(当前面前的方向)移动到相邻的方格。如果前方的方格是障碍物,机器人会返回 。否则,机器人会返回 并移动到到该方格。

你需要编程操纵 Dr. X 的机器人(发送 指令),读取机器人的反馈,并在探索完成后绘制出迷宫的地图。
交互方式
本题为交互题,你的程序通过标准输入输出 (键盘输入、屏幕输出) 与另一个程序交互:
while (true) { string feedback; command = ...; // 确定下一条指令 cout << command << endl; // 输出一条指令 cin >> feedback; // 得到机器人的反馈 if (all_explored) { // 已经探明所有方格 cout << "END" << endl; cout << n << " " << m << endl; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cout << map[i][j]; } cout << endl; } break; // 退出循环 } else { ... // 继续探索 }}即你的代码每次输出一行一条指令(),读取机器人的反馈并逐步探测迷宫,直到全部可达的方格都被探明。探索完成后,程序输出 表示结束,随后输出迷宫的尺寸和地图,空地用点 表示,障碍物用井号 表示。
初始时,Dr. X 并不知道机器人所处的位置。你必须正确探明所有可达的格子,从机器人初始位置不可达的格子,统一认为是障碍物。
输入格式
见「交互方式」。
输出格式
见「交互方式」。
输入输出样例
输入 #1
NFAILWFAILSFAILEFAIL输出 #1
LEFTGOLEFTGOLEFTGOLEFTGOEND3 3####.####说明/提示
数据范围
对于 的数据,迷宫中可达的格子不超过 行、 列。
对于 的数据,迷宫中可达的格子不超过 行、 列。你的程序调用 的次数不得超过 次;你的程序输出的迷宫不得超过 行、 列;未探测到的区域无需输出。
提示
你提交的代码存在交互式的循环:
cout << command << endl; // 输出一条指令cin >> feedback; // 得到机器人的反馈如果手工输入反馈,测试代码就会变得很困难。如果希望测试你的代码,我们建议将输入(cin>>feedback)替换为模拟实现的机器人反馈函数:
cout << command << endl;feedback = get_feedback(command);其中 get_feedback 函数在程序指定得到迷宫中模拟执行 command 命令并返回结果。注意提交前务必将 get_feedback 替换回 cin >> feedback。
解题思路
考查模拟,,交互题型。交互题型需要选手程序与测评程序交互来完成任务的题目,常见的情形和本题相似,选手程序通过向测评程序发出询问,并得到其反馈,通过读取反馈来进行逐步判断并给出最终结果。这种类型题难度较大,在历年或各比赛中并不常见,年曾连续出了两道交互题,评级均为黑。由于篇幅原因,在此只给出本题的代码,有能力的同学可以根据代码去学习解决交互题的思想。
AC代码
#include <bits/stdc++.h>using namespace std;const int N = 310; //不知道机器人的初始位置,防止越界,先扩大迷宫的最大可能范围//将方向字符转换为对应数字{S:0 E:1 N:2 W:3}int get_cur(char wc){ if(wc == 'S') return 0; else if(wc == 'E') return 1; else if(wc == 'N') return 2; else if(wc == 'W') return 3;}int cur; //机器人的当前朝向,0~3对应S, E, N, Wchar c; //接收每次交互指令的返回方向 //调整机器人到目标朝向resvoid turn(int res){ int cnt = (res - cur + 4) % 4; //目标方向-初始方向 //cnt == 0 什么都不做 if(cnt == 1){ cout << "LEFT" << endl; cin >> c; } else if(cnt == 2) { cout << "LEFT" << endl; cin >> c; cout << "LEFT" << endl; cin >> c; } else if(cnt == 3){ cout << "RIGHT" << endl; cin >> c; } cur = res; //更改当前方向 }int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1}; //i:0123对应SENW int g[310][310]; //0:尚未访问 1:可通行 -1:障碍 void dfs(int x, int y){ g[x][y] = 1; for(int i = 0; i < 4; i++){ int nx = x + dx[i], ny = y + dy[i]; if(g[nx][ny] != 0) continue; // 之前已处理过则continue turn(i); //让机器人转到当前i的方向上 cout << "GO" << endl; //询问朝方向i走的结果 string tmp; cin >> tmp; //接收结果 //cout << "DEBUG: tmp = [" << tmp << "]" << endl; // 调试输出 if(tmp == "SUCC"){ dfs(nx, ny); turn(i ^ 2); //回溯时转向相反方向 cout << "GO" << endl; //从哪儿来走回到哪儿 cin >> tmp; //接收,但没啥子用 } else g[nx][ny] = -1; }}int main(){ cout << "LEFT" << endl; //先做一次LEFT询问,目的是得知机器人当前朝向 cin >> c; //读取交互程序返回的方向 cur = get_cur(c); //获取初始方向向左转之后的方向 dfs(N / 2, N / 2); //从中心点开始探索 //计算迷宫的上下左右边界 int up = N, down = 0, left = N, right = 0; for(int i = 1; i < N; i++){ for(int j = 1; j < N; j++){ if(g[i][j] != 0){ //已访问(1)或障碍(-1) up = min(up, i); down = max(down, i); left = min(left, j); right = max(right, j); } } } cout << "END" << endl; //输出行数和列数 cout << down - up + 1 << " " << right - left + 1 << endl; for(int i = up; i <= down; i++){ for(int j = left; j <= right; j++){ if(g[i][j] == 1) cout << "."; else cout << "#"; } cout << endl; } return 0;}[6] 程序套娃
题目描述
套娃,原名 “马特廖什卡娃娃”(Матрешка),起源于 世纪末的俄罗斯,由若干逐渐变小、可以套进彼此的木制空心娃娃组成,象征家庭和生命的延续。套娃因其独特的艺术风格和寓意,逐渐成为俄罗斯民间艺术的代表和著名的旅游纪念品。

Dr. X 想到,程序能不能套娃呢?Dr. X 希望你写一个程序,输入整数 和 ,你的程序会输出一个字符串,Dr. X 会把它保存为 ,然后 Dr. X 会开始玩你的套娃程序:
• 首先,编译运行 ,得到它的屏幕输出,保存为 。 • 在此基础上,编译运行 ,得到它的屏幕输出,保存为 。 • 重复上述过程,编译运行 ,得到它的屏幕输出,保存为 ,直到得到 为止。
Dr. X 期望 不是一个 C++ 程序,而恰好是一个整数( 之后有无换行均可)。
在任何阶段,程序 都只允许向标准输出(屏幕输出)打印,不允许使用操作系统功能,例如创建临时文件等。且这些程序都不得超过 。
输入格式
输入一行两个整数 和 。
输出格式
输出一个可编译的 C++ 程序,它在经历 次编译运行的流程后能输出一个整数 。评测机会采用和你的环境完全相同的编译器(Dev-Cpp 中的 GCC 4.9.2)和编译选项编译 。
输入输出样例
输入 #1
1 99输出 #1
99输入 #2
3 4096输出 #2
#include <iostream>using namespace std;int main() { cout << "#include <iostream>" << endl; cout << "using namespace std;" << endl; cout << "int main() { cout << 4096 << endl; return 0; }" << endl; return 0;}说明/提示
样例 解释
99 会被保存为 。
样例 解释
• Dr. X 会把你程序的输出(样例输出)保存为 。 • 编译运行 ,得到一个 cout << 4096 << endl;的程序,保存为 。• 编译运行 ,得到 4096,保存为 ,符合 的要求。
数据范围
对于 的数据,。
对于 的数据,,。**注意提交程序的大小限制为 。
解题思路
考查递归,转义字符,。其中(奎因)是指一种生成自身源代码的程序,得名于哲学家 。运行该程序后,它的输出结果与程序本身的源代码完全相同,不读取任何外部输入(如文件、网络等)。也可以用递归思想:最终规模的输出需要在规模 的字符串的基础上,前面加上 #include\nint main(){cout<<\",后面加上 \";} 即可。而由于"、\、\n等字符的特殊性,原来的字符串需要再经过一轮转义才能输出正确的结果。
AC代码
#include <iostream>using namespace std;string build(int n, int k){ if(n == 1) return to_string(k); string s = build(n - 1, k); //递归深度-1 //对 s 中的特殊字符进行转义,否则会出错 /* 最终要输出 \"(反斜杠+双引号)→ 源代码里写 \\\" 最终要输出 \\(两个反斜杠)→ 源代码里写 \\\\ 最终要输出 \n(反斜杠+n)→ 源代码里写 \\n */ string res; for(char c : s){ if(c == '\"') res+= "\\\""; else if(c == '\\') res += "\\\\"; else if(c == '\n') res += "\\n"; else res += c; } return "#include <iostream>\nusing namespace std;\n\nint main() {\n cout << \"" + res + "\";\n return 0;\n}";}int main(){ int n, k; cin >> n >> k; cout << build(n, k) << endl; return 0;}