2023 年 12 月青少年软编等考 C 语言八级真题解析

T1. 生理周期

题目链接:SOJ D1151

人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为 23 23 23 天、 28 28 28 天和 33 33 33 天。每一个周期中有一天是高峰。在高峰这天,人会在相应的方面表现出色。例如,智力周期的高峰,人会思维敏捷,精力容易高度集中。因为三个周期的长度不同,所以通常三个周期的高峰不会落在同一天。对于每个人,我们想知道何时三个高峰落在同一天。对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间)。你的任务是给定一个从当年第一天开始数的天数,输出从给定时间开始(不包括给定时间)下一次三个高峰落在同一天的时间(距给定时间的天数)。例如:给定时间为 10 10 10,下次出现三个高峰同天的时间是 12 12 12,则输出 2 2 2(注意这里不是 3 3 3)。

时间限制:1 s
内存限制:64 MB

  • 输入
    一行,包含四个整数: p , e , i p, e, i p,e,i d d d,相邻两个整数之间用单个空格隔开。 p , e , i p, e, i p,e,i 分别表示体力、情感和智力高峰出现的时间(时间从当年的第一天开始计算)。 d d d 是给定的时间,可能小于 p , e p, e p,e i i i。所有给定时间是非负的并且小于等于 365 365 365,所求的时间小于等于 21252 21252 21252
  • 输出
    一个整数,即从给定时间起,下一次三个高峰同天的时间(距离给定时间的天数)。
  • 样例输入
    4 5 6 7
    
  • 样例输出
    16994
    

思路分析

此题考查枚举法,属于入门题。

枚举 d + 1 ∼ d + 21252 d+1\sim d+21252 d+1d+21252 的每一个数 k k k,检测是否满足体力、感情和智力同时出现高峰,即对各自周期取余后分别为 p , e , i p,e,i p,e,i,若找到,则输出 k − d k-d kd 即为答案。注意到 p , e , i p,e,i p,e,i 并不一定是第一次出现高峰的时间,因此 p , e , i p,e,i p,e,i 应当先分别对各自的周期取余。

/*
 * Name: T1.cpp
 * Problem: 生理周期
 * Author: Teacher Gao.
 * Date&Time: 2024/11/24 14:26
 */

#include <iostream>

using namespace std;

int main()
{
   
   
	ios::sync_with_stdio(false);
	cin.tie(0);

	int p, e, i, d;
	cin >> p >> e >> i >> d;

	p %= 23, e %= 28, i %= 33;
	
	for (int k = d+1; k <= 21252; k++) {
   
   
		if (k%23 == p && k%28 == e && k%33 == i) {
   
   
			cout << k - d;
			break;
		}
	}

	return 0;
}

T2. 开关问题

题目链接:SOJ D1234

N N N 个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后 N N N 个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。(不计开关操作的顺序)

时间限制:1 s
内存限制:64 MB

  • 输入
    输入第一行有一个数 K K K,表示以下有 K K K 组测试数据。
    每组测试数据的格式如下:
    第一行一个数 N N N 0 < N < 29 0 < N < 29 0<N<29);
    第二行 N N N 0 0 0 或者 1 1 1 的数,表示开始时 N N N 个开关状态。
    第三行 N N N 0 0 0 或者 1 1 1 的数,表示操作结束后 N N N 个开关的状态。
    接下来,每行两个数 I   J I\ J I J,表示如果操作第 I I I 个开关,第 J J J 个开关的状态也会变化。每组数据以 0   0 0\ 0 0 0 结束。
  • 输出
    如果有可行方法,输出总数,否则输出 Oh,it's impossible~!!
  • 样例输入
    2
    3
    0 0 0
    1 1 1
    1 2
    1 3
    2 1
    2 3
    3 1
    3 2
    0 0
    3
    0 0 0
    1 0 1
    1 2
    2 1
    0 0
    
  • 样例输出
    4
    Oh,it's impossible~!!
    
  • 提示
    第一组数据的说明:一共以下四种方法:
  • 操作开关 1 1 1
  • 操作开关 2 2 2
  • 操作开关 3 3 3
  • 操作开关 1 1 1 2 2 2 3 3 3(不记顺序)

思路分析

此题考查双向搜索,属于模板题。

容易想到暴力枚举,即依次考虑每个开关的状态是否修改,时间复杂度为 O ( 2 n ) O(2^n) O(2n)。如果从初始状态和目标状态双向搜索,时间复杂度可以降低到 O ( 2 n 2 ) O(2^{\frac{n}{2}}) O(22n),足以应对此题。由于开关总数不超过 28 28 28,可以进行状态压缩,即使用一个 32 32 32 位整型变量来标记每一个开关的状态。

至于开关之间的联动,设置一个数组 a [ i ] a[i] a[i] 来存储修改开关 i i i 之后会发生变化的所有开关,如果会发生变化则将对应二进制位设置为 1 1 1,初始时 a[i] = 1 << i,表示只会修改自己。

/*
 * Name: T2_1.cpp
 * Problem: 开关问题
 * Author: Teacher Gao.
 * Date&Time: 2025/07/11 18:09
 */

#include <bits/stdc++.h>

using namespace std;

int n, ans;
int a[30];
unordered_map<int, int> mp;

void DFS1(int now, int state) {
   
   
	if (now == n / 2 + 1) {
   
   		// 暴力枚举前一半
		mp[state]++; return ;
	}
	
	DFS1(now + 1, state);		// 编号为 now 的开关状态不变
	
	state ^= a[now];			// 状态修改
	DFS1(now + 1, state);
}

void DFS2(int now, int state) {
   
   
	if (now == n / 2) {
   
   			// 暴力枚举后一半
		ans += mp[state]; return ;
	}

	DFS2(now - 1, state);

	state ^= a[now];
	DFS2(now - 1, state);
}

int main()
{
   
   
	ios::sync_with_stdio(false);
	cin.tie(0);

	int k, x, y;
	cin >> k;

	while (k--) {
   
   
		int s = 0, e = 0, t;
		ans = 0;
        mp.clear();
		memset(a, 0, sizeof(a));

		cin >> n;
		for (int i = 1; i <= n; i++) {
   
   
			cin >> t;
			s |= t << i;
		}
		for (int i = 1; i <= n; i++) {
   
   
			cin >> t;
			e |= t << i;
            a[i] = 1 << i;
		}

		while (cin >> x >> y && x + y) a[x] |= 1 << y;

		DFS1(1, s);
		DFS2(n, e);

		if (ans != 0) cout << ans << "\n";
		else cout << "Oh,it's impossible~!!\n"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南朔 Clancy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值