计算机复试学习笔记 Day45

133. FJ的字符串

题目

问题描述

FJ在沙盘上写了这样一些字符串:

A1 = "A"

A2 = "ABA"

A3 = "ABACABA"

A4 = "ABACABADABACABA"

... ...

你能找出其中的规律并写所有的数列AN吗?

输入说明

仅有一个数:N ≤ 20。

输出说明

请输出相应的字符串AN,以一个换行符结束。输出中不得含有多余的空格或换行、回车符。

个人总结

思路

  1. 通过观察规律可以发现,当前字符串是由两边相同的子串拼接上中间递增的字母组成的,即 第n个字符串 = 第n-1个字符串 + 第n个大写字母 + 第n-1个字符串。
  2. 利用递归思想,定义处理第n层情况的函数。先递归打印左边部分,再打印中间的当前字符,最后递归打印右边部分。
  3. 递归边界为n等于1时,直接打印字母A并一层层返回。
  4. 由于题目输入最大为20,最终字符串长度达到2的20次方减1,大约是一百万个字符。如果先用字符串变量拼凑再输出,会导致不必要的内存消耗和拼接耗时。代码中采用边递归边直接输出的方式,是非常高效的做法。

易错点

  1. 计算中间字符时,第n层对应的字母偏移量应该是n减1。不要直接加上n,否则会跳过原本应该输出的字母。
  2. 必须在递归函数最前面写好终止条件(n等于1直接输出A并返回),否则会导致无限死循环。

代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

void dfs(int n) {
	if (n == 1) {
		cout << "A";
		return;
	}
	if (n == 0) return;
	
	dfs(n-1);
	char c = 'A' + n - 1;
	cout << c;
	dfs(n-1);
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	
	int N;
	cin >> N;
	dfs(N);
	cout << "\n";
	
	
	return 0;
}

134. 3000米排名预测

题目

问题描述

3000米长跑时,围观党们兴高采烈地预测着最后的排名。因为他们来自不同的班,对所有运动员不一定都了解,于是他们分别对自己了解的一些运动员的实力作出了评估,即对部分运动员做了相对排名的预测,并且告诉了可怜留守的班长。因为无聊,于是他们就组团去打Dota去了。比赛结束后他们向班长询问最后的排名,但班长不记得了,只记得他们中哪些人的预测是正确的,哪些人的预测是错误的。他们想知道比赛的排名可能是什么。

输入说明

第一行两个整数n, m,n为运动员数量,m为围观党数量。运动员编号从0到n-1。

接下来m行,每行为一个围观党的相对排名预测。每行第一个数c表示他预测的人数,后面跟着c个0~n-1的不同的数,表示他预测的运动员相对排名,最后还有一个数,0表示这个预测是错误的,1表示是正确的。

数据规模和约定

1<=n<=10, 2<=c<=n, 1<=m<=10,保证数据合法,且答案中排名可能数不超过20000。对于一个排名序列,一个预测是正确的,当且仅当预测的排名的相对顺序是排名序列的一个子序列。一个预测是错误的,当且仅当这个预测不正确。

输出说明

第一行一个数k为有多少种排名的可能。

下面k行,每行一个0~n-1的排列,为某一个可能的排名,每个数字后有一个空格(即:行尾也有一个空格)。所有排名按字典序依次输出。

比如,输入:

3 2

2 0 1 1

2 2 1 0

则输出:

1

0 1 2

个人总结

思路

读取运动员数量n和预测数量m,然后读取每个预测的信息,包括预测涉及的运动员数量c、运动员编号序列和正确性标志yes(1为正确,0为错误)。

初始化一个从0到n-1的有序序列perm作为起点,使用do-while循环结合next_permutation生成所有可能的排列。

对于每个生成的排列,遍历所有预测,使用check函数判断该排列中预测的运动员序列是否严格符合子序列匹配(即相对顺序一致)。

check函数通过双指针i和j遍历:j扫描整个perm,当perm[j]匹配预测序列的当前元素p.ranks[i]时i递增;最终若i等于c则表示匹配成功,返回true,否则false。

如果一个排列的所有预测匹配结果与各自的yes标志一致,则视为有效,记录到结果列表res中。

最后输出有效排列的数量k,并按字典序输出每个有效排列。

易错点

  • 在 while (j < n && i < p.c) 循环中,如果不加 i < p.c,当 i 已经达到 p.c(即预测序列已经找齐了)之后,若此时 j 还没走完,代码会访问 p.ranks[p.c]。由于在该结构体中 ranks 数组长度定义为 10,且输入数据可能不足 10 个,越界访问到的可能是 0 或者内存中相邻的其他变量。这会导致 i 有可能继续增加,从而使得最后 i == p.c 的判断失效。加上限制后,一旦 i 找完了所有预测数字,循环立即停止,此时 i 恰好等于 p.c,逻辑严谨。
  • next_permutation需从有序序列起始,否则无法覆盖所有排列;同时do-while结构确保首尾均包含。

代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

struct Pred {
	int c;
	int ranks[10] = {0};
	bool yes;
};

vector<Pred> preds;
int n, m, c;

bool check(Pred &p, vector<int> &perm) {
	// 检测该预测是否符合本序列
	int i = 0, j = 0;
	
	// 按照预测顺序逐个向后匹配
	while (j < n && i < p.c) {
		if (p.ranks[i] == perm[j]) {
			i++;
		}
		j++;
	}
	
	// 如果预测完全匹配, i 应该和预测数量相等
	return i == p.c;
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	
	// 输入
	cin >> n >> m;
	for (int i = 0; i < m; i++) {
		Pred p;
		cin >> c;
		p.c = c;
		for (int j = 0; j < c; j++) {
			cin >> p.ranks[j];
		}
		cin >> p.yes;
		preds.push_back(p);
	}
	
	// 构造初始序列
	vector<int> perm(n);
	for (int i = 0; i < n; i++) perm[i] = i;
	
	// 结果变量
	int k = 0;
	vector<vector<int>> res;
	
	// 验证每个排列
	do {
		bool valid = true;
		for (auto &p : preds) {
			// 检查预测对错与实际对错是否相符
			if (check(p,perm) != p.yes) {
				valid = false;
				break;
			}
		}
		
		// 记录有效结果
		if (valid) {
			k++;
			res.push_back(perm);
		}
	} while (next_permutation(perm.begin(), perm.end()));
	
	// 输出
	cout << k << "\n";
	for (auto &p : res) {
		for (int num : p) {
			cout << num << " ";
		}
		cout << "\n";
	}
	
	return 0;
}

135. 芯片测试

题目

问题描述

有n(2≤n≤20)块芯片,有好有坏,已知好芯片比坏芯片多。

每个芯片都能用来测试其他芯片。用好芯片测试其他芯片时,能正确给出被测试芯片是好还是坏。而用坏芯片测试其他芯片时,会随机给出好或是坏的测试结果(即此结果与被测试芯片实际的好坏无关)。

给出所有芯片的测试结果,问哪些芯片是好芯片。

输入说明

输入数据第一行为一个整数n,表示芯片个数。

第二行到第n+1行为n*n的一张表,每行n个数据。表中的每个数据为0或1,在这n行中的第i行第j列(1≤i, j≤n)的数据表示用第i块芯片测试第j块芯片时得到的测试结果,1表示好,0表示坏,i=j时一律为1(并不表示该芯片对本身的测试结果。芯片不能对本身进行测试)。

输出说明

按从小到大的顺序输出所有好芯片的编号

个人总结

思路

这道题目的思路很简单,核心就是一点:正确的检测结果是固定的。如果一块芯片是好的,那么它所标记出的所有好芯片也是好芯片,都会和它显示一模一样的结果,也就是正确结果。如果出现不一致,那他一定是坏芯片。

因此整体流程就是,逐个检查每一行的结果。假设这行是好的,看它标出来的好芯片是否都和它给出的检测结果一模一样。如果不一致,就跳过,看下一个。如果检测结果一致,就代表这是好芯片。只要找到一个好芯片,它就已经自带了所有芯片的结果,可以停止循环直接输出结果。

在解题过程中我们还可以注意到几个对实际做题没有影响但有趣的小细节:

  1. 会不会出现一个坏芯片,明明是在随机显示结果,但是恰好全都随机对了的情况呢?不会。一方面是哪有这么巧的事,另一方面(认真的讲)那就是题目里有个细节说,芯片对自己的结果都显示为 1 .也就是说,坏芯片会固定把自己标为好的,那他一定会和其他好芯片的检测结果不一致,然后被淘汰。
  2. 这个把自己固定标为 1 对好芯片有没有影响呢?没有,因为他确实是好的,那说自己是好的也没有问题,相反,这个样子保证了所有好芯片的检测结果一模一样,写代码更方便了,不需要对对角线做任何特殊处理。
  3. 如果出现坏芯片把所有其他坏芯片都标成是好芯片,好芯片标成坏芯片,而且所有的坏芯片还坏得很一致,团结的显示了一模一样的错误结果,那这个算法不就炸了吗?
    1. 对,是这样,当前的代码处理不了这种情况,但是正如我之前所说的,哪有这么巧的测试数据,反正这题交上去通过了。
    2. 当然非要处理也不是不行,加一小段计数代码就行,看标出来的好芯片数量有没有超过一半,没有就认为这个芯片是错的继续往下找,就能防御这个情况。
    3. 原理:坏芯片一致伪装永远都不可能超过一半。坏芯片不能说好芯片是对的,否则一旦检测到好芯片就会暴露,所以坏芯片只能说其他坏芯片是对的,而坏芯片不超过一半,所以只要出现这种情况就计数就会小于一半。
  4. 关于效率问题,这个算法得出正确结果的速度意外的挺快。因为好芯片比坏芯片多,大概率前几个就能找到结果了,即使在最糟糕的情况下,到达 n/2 行时一定能找到结果。不过这也不是什么数量级上的区别,意义也不是很大。

另一种解题思路(哈基米提供):

在输入给我们的 n×n 矩阵中:

  • 行(Row) :代表某个芯片去测试别人的结果。
  • 列(Col) :代表某个芯片被别人测试的结果。

如果我们从"行"的角度去分析,会发现坏芯片给出的测试结果是随机的,我们很难直接判定这行数据可不可信。所以我们需要转换视角,从**"列"(即被测试的芯片)**的角度去分析。

因为好芯片的数量严格大于总数的一半 (即大于 n/2),并且好芯片的测试结果永远真实,我们可以推导出以下结论:

  1. 假设第 jjj 个芯片是"好芯片"

    那么在使用所有的好芯片去测试它时,好芯片不管在什么情况下都会给出 1(好的)。又因为好芯片的数量占据了总数的一半以上,所以在这个芯片所对应的第 j 列中,数字 1 的个数绝对会超过 n/2

  2. 假设第 jjj 个芯片是"坏芯片"

    那么在使用所有的好芯片去测试它时,好芯片必定识破它的伪装,给出 0(坏的)。这也就意味着在这一列中,数字 0 的个数占据了一半以上,反推下来,其对应的第 j 列中,数字 1 的个数绝对不可能超过 n/2

最终结论: 不需要进行任何复杂的图论论证或 DFS 搜索,只要统计对于每个芯片(在矩阵中对应一整列),评价它为好(即数字为 1)的数量。如果大于 n/2,那它就是好芯片;反之就是坏芯片。

代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	
	int n;
	cin >> n;
	
	vector<vector<int>> tests(n,vector<int>(n));
	vector<int> res(n);
	
	
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> tests[i][j];
		}
	}
	
	int row = 0, col = 0;
	while (true) {
		// 假设本行正确并验证
		res = tests[row];
		bool wrong = false;
		for (col = 0; col < n; col++) {
			// 正确的芯片检测出的所有正确芯片,应该与本芯片结果相同
			if (tests[row][col] == 1 && tests[col] != res) {
				wrong = true;
				break;
			}
		}
		if (wrong) {
			row++;
		} else {
			// 找到正确结果,不用再找了
			break;
		}
	}
	
	// 输出
	for (int i = 0; i < n; i++) {
		if (res[i] == 1) {
			cout << i + 1 << " ";
		}
	}
	cout << "\n";
	
	return 0;
}

计算机英语

翻译练习

Overfitting is a common problem during the training of machine learning models. When a model performs extremely well on training data but poorly on test data, it is considered to suffer from overfitting. This usually occurs when the model is too complex or when the amount of training data is insufficient. To reduce overfitting, researchers have proposed various techniques such as regularization , data augmentation, and cross-validation . Regularization methods introduce penalty terms into the loss function to limit the magnitude of model parameters, thereby making the model simpler and more stable. Data augmentation increases the diversity of training data by applying operations such as rotation, cropping, or adding noise to the original data. In addition, cross-validation evaluates the generalization ability of a model by repeatedly splitting the dataset into training and validation sets. These techniques can effectively improve the performance of machine learning models in real-world applications.

过拟合是机器学习模型训练中的一个常见问题。当模型在训练数据上表现极其出色,但在测试数据上表现不佳时,就被认为存在过拟合。这通常发生在模型过于复杂或训练数据量不足的情况下。为了缓解过拟合,研究人员提出了多种技术,如正则化、数据增强和交叉验证。正则化方法通过在损失函数中引入惩罚项来限制模型参数的幅度,从而使模型更简单、更稳定。数据增强通过对原始数据进行旋转、裁剪或添加噪声等操作,来增加训练数据的多样性。此外,交叉验证通过将数据集反复划分为训练集和验证集来评估模型的泛化能力。这些技术可以有效提升机器学习模型在实际应用中的性能。

背单词打卡

相关推荐
bIo7lyA8v5 小时前
算法稳定性分析中的数值误差传播机制的技术5
算法
生信研究猿5 小时前
leetcode 121.买卖股票的最佳时机
算法·leetcode·职场和发展
Chef_Chen5 小时前
Agent学习-RAG--上下文压缩与知识库的更新
人工智能·学习·自然语言处理
CoovallyAIHub5 小时前
不需要Memory Bank:CMDR-IAD用2D+3D双分支重建做工业异常检测,MVTec 3D 97.3%
算法·架构·github
计算机学姐5 小时前
基于SpringBoot的在线学习网站平台【个性化推荐+数据可视化+课程章节学习】
java·vue.js·spring boot·后端·学习·mysql·信息可视化
Engineer邓祥浩5 小时前
JVM学习笔记(7) 第三部分 虚拟机执行子系统 第6章 类文件结构
jvm·笔记·学习
中屹指纹浏览器5 小时前
2026指纹浏览器技术架构深度解析:从隔离原理到性能优化的全链路实践
经验分享·笔记
AI科技星5 小时前
基于四维速率恒为c公设的北斗GEO卫星昼夜钟差模型修正与实测验证
开发语言·人工智能·线性代数·算法·数学建模
sheeta19985 小时前
LeetCode 每日一题笔记 日期:2026.04.09 题目:3655.区间乘法查询后的异或二
笔记·算法·leetcode
何伯特5 小时前
STTR算法详解:用Transformer重新定义立体匹配
深度学习·算法·transformer