历年CSP-S初赛真题解析 | 2025年CSP-S初赛阅读程序(16-33)

​欢迎大家订阅我的专栏:算法题解:C++与Python实现
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!

专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。

适合人群:

  • 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
  • 希望系统学习C++/Python编程的初学者
  • 想要提升算法与编程能力的编程爱好者

附上汇总贴:历年CSP-S初赛真题解析 | 汇总-CSDN博客


#include <algorithm>
#include <cstdio>
#include <cstring>
bool flag[27];
int n;
int p[27];
int ans = 0;
void dfs(int k) {
	if (k == n+1) {
		++ans;
		return;
	}
	for (int i=1; i<=n; ++i) {
		if (flag[i]) continue;
		if (k>1 && i == p[k-1] +1) continue;
		p[k] = i;
		flag[i] = true;
		dfs(k+1);
		flag[i] = false;
	}
	return;
}
int main() {
	scanf("%d", &n);
	dfs(1);
	printf("%d\n", ans);
	return 0;
}

第16题

当输入的n=3的时候,程序输出的答案为3。( )

A.正确

B.错误

【答案】:A

【解析】

不出现相邻位置数字连续(递增1)的排列有:132,213,321

第17题

在dfs函数运行过程中,k的取值会满足1≤k≤n+1。( )

A.正确

B.错误

【答案】:A

【解析】

在调用dfs(1)时,k的初值为1,随后不断通过递归加一变成 n+1后,到达递归出口不再继续加一。

第18题

删除第19行的"flag[i]=false;",对答案不会产生影响。( )

A.正确

B.错误

【答案】:B

【解析】

当枚举第k个位置选择放数字i时,通过flag[i]=true进行标记,防止数字 i 在后面的位置被再一次放上。而从i枚举到i+1后,原本放在位置k的数字i,它的标记需要撤销,才能保证后续能够正确地判断数字是否放置过。

第19题

当输入的n=4的时候,程序输出的答案为。( )

A.11

B.12

C.24

D.9

【答案】:A

【解析】

枚举出所有24种排列,排除掉不合法的情况即可。

第20题

如果因为某些问题,导致程序运行第25行的 dfs 函数之前,数组p的初值并不全为0,则对程序的影响是( )。

A.输出的答案比原答案要小

B.无法确定输出的答案

C.程序可能陷入死循环

D.没有影响

【答案】:D

【解析】

p[ ] 数组的主要功能是用于在第15行去判断是否出现连续数字,而p[k-1]的值会在之前的dfs(k-1)中会被重新赋值,原本存在的值不会造成任何影响。

【答案】D

第21题

假如删去第14行的"if(flag[i]) continue;",输入3,得到的输出答案是( )。

A.27

B.3

C.16

D.12

【答案】:

【解析】

去掉后无需保证每个数字i是否在之前放置过,若不考虑连续数字的限制,可以直接枚举出所有3^3=27种情况,然后去除掉不合法情况即可。

#include <algorithm>
#include <cstdio>
#include <cstring>
#define ll long long
int cnt_broken = 0;
int cnt_check = 0;
int n, k;
inline bool check(int h) {
	printf("now check:%d\n", h);
	++cnt_check;
	if (cnt_broken == 2) {
		printf("You have no egg!\n");
		return false;
	}
	if (h >= k) {
		++cnt_broken;
		return true;
	} else {
		return false;
	}
}
inline bool assert_ans(int h) {
	if (h == k) {
		printf("You are Right using %d checks\n", cnt_check);
		return true;
	} else {
		printf("Wrong answer!\n");
		return false;
	}
}
inline void guess1(int n) {
	for (int i=1; i<=n; ++i) {
		if (check(i)) {
			assert_ans(i);
			return;
		}
	}
}
inline void guess2(int n) {
	int w = 0;
	for (w=1; w * (w+1)/2<n; ++w)
		;
	for (int ti = w, nh = w;; --ti, nh += ti, nh = std::min(nh, n)) {
		if (check(nh)) {
			for (int j = nh - ti + 1; j < nh; ++j) {
				if (check(j)) {
					assert_ans(j);
					return;
				}
			}
		assert_ans(nh);
		return;
		}
	}
}
int main() {
	scanf("%d%d", &n, &k);
	int t;
	scanf("%d", &t);
	if (t == 1) {
		guess1(n);
	} else {
		guess2(n);
	}
	return 0;
}

第22题

当输入为"6 5 1"时,猜测次数为5;当输入"6 5 2"时,猜测次数为3。( )

A.正确

B.错误

【答案】:A

【解析】

guess1函数会从1开始逐层枚举并检验,当i=5时结束,猜测次数为5。 guess2函数则是先将6层楼分成了3块,每块分别有3,2,1层。首先猜测3没碎,然后猜测5碎了。然后使用第2个鸡蛋,从4开始继续猜测,没碎,5不用猜测了。总猜测次数是3次。

第23题

不管输入的n和k具体为多少,t=2时的猜测数总是小于等于t=1时的猜测数。( )

A.正确

B.错误

【答案】:B

【解析】

考虑k=1,n=2的边界情况。t=1时猜测次数为k=1;而t=2时会先猜测2再猜测1,共2次操作。

第24题

不管t=1或t=2,程序都一定会猜到正确结果。( )

A.正确

B.错误

【答案】:A

【解析】

通过程序的整体分析可以知道两个函数的具体功能,它们都不会让鸡蛋的破碎次数超过2次。

第25题

函数guess1在运行过程中,cnt broken的值最多为( )。

A.0

B.1

C.2

D.n

【解析】guess1函数是暴力枚举每层去检测,当i=k时,鸡蛋会发生唯――次破碎,同时也得到了答案,程序结束。

【答案】B

第26题

函数guess2在运行过程中,最多使用的猜测次数的量级为( )。

A. O ( n ) O(n) O(n)

B. O ( n 2 ) O(n^2) O(n2)

C. O ( n ) O(\sqrt n) O(n )

D. O ( l o g n ) O(log n) O(logn)

【答案】:C

【解析】

由w*(w-1)<n的条件可以知道,w的量级是 O ( n ) O(\sqrt n) O(n ),它的含义是将n层楼分成了w个块。第43行for循环枚举每个块检查的时候,鸡蛋在其中一个块破碎后会马上进入到这个块去检查每一层。已知每块中的层数也是w的级别,因此总共的量级是 O ( n ) O(\sqrt n) O(n )

第27题

当输入的n=100的时候,代码中t=1和t=2分别需要的猜测次数最多分别为( )。

A.100,14

B.100,13

C.99,144

D.99,13

【答案】:A

【解析】

guess1的猜测次数与有关,k最大取到100。guess2 中会先将100层分为14,13,…,3,2,1等若干块。当k=14时,正好会将前14层楼各猜测一次。

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#define ll long long
int n, m;
std::vector<int> k, p;
inline int mpow(int x, int k) {
	int ans = 1;
	for (; k; k = k >> 1, x = x * x) {
		if (k & 1) 
			ans = ans * x;
	}
	return ans;
}
std::vector<int> ans1, ans2;
int cnt1, cnt2;
inline void dfs(std::vector<int>& ans, int& cnt, int l, int r, int v) {
	if (l > r) {
		++cnt;
		ans.push_back(v);
		return;
	}
	for (int i=  ; ++i) {
		dfs(ans, cnt, l+1, r, v+k[l]*mpow(i, p[l]));
	}
	return;
}
std::vector<int> cntans1;
int main() {
	scanf("%d%d", &n, &m);
	k.resize(n + 1);
	p.resize(n + 1);
	for (int i=1; i<=n; ++i) {
		scanf("%d%d", &k[i], &p[i]);
	}
	dfs(ans1, cnt1, 1, n >> 1, 0);
	dfs(ans2, cnt2, (n>>1) + 1, n, 0);
	std::sort(ans1.begin(), ans1.end());
	int newcnt1 = 1;
	cntans1.push_back(1);
	for (int i=1; i<cnt1; ++i) {
		if (ans1[i] == ans1[newcnt1 - 1]) {
			++cntans1[newcnt1 - 1];
		} else {
			ans1[newcnt1++] = ans1[i];
			cntans1.push_back(1);
		}
	}
	cnt1 = newcnt1;
	std::sort(ans2.begin(), ans2.end());
	int las = 0;
	ll ans = 0;
	for (int i=cnt2-1; i>=0; --i) {
		for (; las<cnt1 && ans1[las] + ans2[i] < 0; ++las)
			;
		if (las<cnt1 && ans1[las] + ans2[i] == 0)
			ans += cntans1[las];
	}
	printf("%lld\n", ans);
	return 0;
}

第28题

删除第51行的"std::sort(ans2.begin(), ans2.end()); "后,代码输出的结果不会受到影响。( )

A.正确

B.错误

【答案】:B

【解析】

第54行使用的是尺取法,需要保证ans2[i]是从大到小枚举,否则las 没有单调性,无法从小到大查找 ans1。

第29题

假设计算过程中不发生溢出,函数mpow(x, k)的功能是求出 x^k 的取值。( )

A.正确

B.错误

【答案】:A

【解析】

mpow函数的功能是使用二分求快速幂。

第30题

代码中第39行到第50行的目的是为了将ans1数组进行"去重"操作。( )

A.正确

B.错误

【答案】:A

【解析】

ans2[i]对应的 ans1 可能不止一个,为了降低时间复杂度,只能预处理ans1 中所有相同的值。

第31题

当输入为"3 15 1 2 -1 2 1 2"时,输出结果为( )。

A.4

B.8

C.0

D.10

【答案】:B

【解析】

把输入代入进去,得到的式子是x²-y²+z²=0,其中1≤x,y,≤15,其中(x,z,y)就是勾股定理三元组,符合条件的只有(3,4,5),(4,3,5),(6,8,10),(8,6,10),(9,12,15),(12,9,15),(5,12,13),(12,5,13)共八组。

第32题

记程序结束前p数组元素的最大值为P,则该代码的时间复杂度是( )。

A. O ( n ) O(n) O(n)

B. O ( m n l o g m n ) O(m^nlogm^n) O(mnlogmn)

C. O ( m n / 2 l o g m n / 2 ) O(m^{n/2}logm^{n/2}) O(mn/2logmn/2)

D. O ( m n / 2 ( l o g m n / 2 + l o g P ) ) O(m^{n/2}(logm^{n/2}+logP)) O(mn/2(logmn/2+logP))

【答案】:D

【解析】

每次dfs会生成 m n / 2 m^{n/2} mn/2 个值,每次递归都要调用一次 mpow,一次快速幂的时间复杂度为 O ( l o g P ) O(logP) O(logP),但是 dfs 的时间复杂度主要看最后一层,因此 dfs 的总复杂度为 O ( m n / 2 × l o g P ) O(m^{n/2}\times logP) O(mn/2×logP)

之后使用 sort 对 ans1、ans2 排序,时间复杂度为 O ( m n / 2 × l o g m n / 2 ) O(m^{n/2}\times logm^{n/2}) O(mn/2×logmn/2)

之后的去重、尺取法均为线性算法,时间复杂度为 O ( m n / 2 ) O(m^{n/2}) O(mn/2)

综上,因为不知道 P 和 m 谁更大,所以不能省略,整个程序的时间复杂度是 O ( m n / 2 ( l o g m n / 2 + l o g P ) ) O(m^{n/2}(logm^{n/2}+logP)) O(mn/2(logmn/2+logP))

第33题

本题所求出的是( )。

A.满足 a , b , c ∈ [ 1 , m ] a,b,c \in [1,m] abc[1m] 的整数方程 a 3 + b 3 = c 3 a^3+b^3=c^3 a3+b3=c3 解的数量

B.满足 a , b , c ∈ [ 1 , m ] a,b,c \in [1,m] abc[1m] 的整数方程 a 2 + b 2 = c 2 a^2+b^2=c^2 a2+b2=c2 解的数量

C.满足 x i ∈ [ 0 , m ] xi\in [0,m] xi[0m] 的整数方程 ∑ i = 1 n k i ⋅ x i p i = 0 \sum_{i=1}^nk_i\cdot x_i^{p_i}=0 i=1nkixipi=0 的解的数量

D.满足 x i ∈ [ 1 , m ] xi\in [1,m] xi[1m] 的整数方程 ∑ i = 1 n k i ⋅ x i p i = 0 \sum_{i=1}^nk_i\cdot x_i^{p_i}=0 i=1nkixipi=0 的解的数量

【答案】:D

【解析】

根据第 37 行 dfs 的系数是 n>>1,可以确定未知数个数一定是n个。根据第24行 i 从 1 到 m 枚举,可以确定未知数的取值范围是1到m。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值