2024 保研夏令营机试刷题记录
刷 leetcode 还是没什么意思,准备一天来一套真题(笑,不做特别难的题和特别麻烦的大模拟,确保做题速度。
会按照一个顺序选择刷题。
2023 研究生推免 (5/14)
全在其中
A:全在其中
总时间限制: 1000ms 内存限制: 65536kB 描述 你设计了一个新的加密技术,可以用一种聪明的方式在一个字符串的字符间插入随机的字符串从而对信息进行编码。由于专利问题,我们将不会详细讨论如何在原有信息中产生和插入字符串。不过,为了验证你的方法,有必要写一个程序来验证原来的信息是否全在最后的字符串之中。
给定两个字符串s和t,你需要判断s是否是t的“子列”。也就是说,如果你去掉t中的某些字符,剩下字符将连接而成为s。
输入 输入包括多个测试样例。每一个都是由空格分隔的由字母数字ASCII字符组成的两个特定的字符串s和t。s和t的长度不超过100000。
输出 对于每个测试样例,如果s是t的“子列”,则输出”Yes”,否则输出”No”
签到题。双指针扫一遍即可。
#include <iostream>
#include <string>
using namespace std;
int n;
bool isSub(string str, string sub) {
int i = 0, l = 0;
while (i < sub.size() && l < str.size()) {
if (str[l] == sub[i]) i++, l++;
else l++;
}
return i == sub.size();
}
int main() {
string substr, str;
while (cin >> substr >> str) {
cout << (isSub(str, substr) ? "Yes" : "No" ) << endl;
}
}
最接近的分数
B:最接近的分数 总时间限制: 1000ms 内存限制: 65536kB 描述
分母不超过 N 且 小于 A/B 的最大最简分数是多少?
输入
三个正整数N,A,B,相邻两个数之间用单个空格隔开。。
输出
两个正整数,分别是所求分数的分子和分母,中间用单个空格隔开。
从 1~N 扫一遍,选择分子和分母。因为 ,所以 可以接受。
注意就是用乘法防止除法精度问题。最大 1000*1000,爆不了 int
#include <iostream>
using namespace std;
int n, a, b;
int main() {
cin >> n >> a >> b;
// 分母小于等于 n,小于 a / b
int mxi = -1, mxj = -1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
// j/i
if (j*b < a *i) {
if (mxi == -1 || mxj == -1 || j * mxi > mxj * i) mxj = j, mxi = i;
}
}
}
cout << mxj << " " << mxi << endl;
}
踩方格
踩方格
总时间限制: 1000ms 内存限制: 65536kB 描述
有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设: a. 每走一步时,只能从当前方格移动一格,走到某个相邻的方格上; b. 走过的格子立即塌陷无法再走第二次; c. 只能向北、东、西三个方向走;
请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。
输入 允许在方格上行走的步数n(n \le 20)
输出 计算出的方案数量
一眼 DP 或者 dfs,用 DFS 写的回溯,最多运行 20 * 3 次。这里开了个 41 * 21 的数组,然后从 20 开始走,数组用于判断走没走过。
#include <iostream>
using namespace std;
int mat[42][21];
int res;
int n;
int dx[3] = {0, 1, 0};
int dy[3] = {-1, 0, 1};
void dfs(int x, int y, int u) {
if (u == n) {
res ++;
return;
}
for (int i = 0; i < 3; i++) {
int xx = x + dx[i];
int yy = y + dy[i];
if (mat[xx][yy]) continue;
mat[xx][yy] = 1;
dfs(xx, yy, u + 1);
mat[xx][yy] = 0;
}
}
int main() {
cin >> n;
mat[20][0] = 1;
dfs(20, 0, 0);
cout << res;
}
核电站
D:核电站
总时间限制: 5000ms 单个测试点时间限制: 1000ms 内存限制: 131072kB 描述 一个核电站有 N 个放核物质的坑,坑排列在一条直线上。如果连续 M 个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。
任务:对于给定的 N 和 M ,求不发生爆炸的放置核物质的方案总数。
输入 只有一行,两个正整数 N ,M (1 < N < 50, )。
输出 一个正整数 S,表示方案总数。
2 \le M \le 5最卡的一题。一开始也用了一个 DFS,每个位置摆和不摆两种。写完发现是 的复杂度,寄!不过如果真遇到应该可以骗几个测例的分。
选择 DP,一开始写了一个二维 DP,但是一直没有想清楚转移方程。
代表到 i 位置合法的个数,j 是二维的,代表第 i 位置放或者不放。对于 i < m 的情况,直接加就完事了;对于 i == m 的情况,减掉第一个位置放的一种情况;对于 i > m 的情况,这里一直没想清楚,后面突然理解了,我们要去掉的情况是明朗的:i-m+1 ~ i 共 m - 1 个位置都放,i-m 这个位置不放,而对于前面 i-m-1 个位置,我们怎么放都可以,因此不合法的个数是 1 * f[i-m-1] 个。
然后就是要注意爆 int 的问题,假设 m 不存在,那么我们就有 2^n 个方案,就算有 m 这个限制条件,也不会减特别多,所以用 long long 最保险。
/* O(2^n) */
void dfs(int x, int u, int p) {
// u 当前已放置的个数
// x 当前放置的位置
// p 当前连续放置多少个
if (p >= m) return;
if (x == n) {
res ++;
return;
}
dfs(x + 1, u, 0); // 不在这里放
dfs(x + 1, u + 1, p + 1); // 在这里放
return;
}
long long f[55];
int main() {
cin >> n >> m;
// f[i][0/1] 代表在 i 位置放和不放的总数情况 (合法的)
// f[n][0] + f[n][1]
// f[i][0] = f[i-1][0] + f[i-1][1]
// f[i][1] = f[i-1][0] + f[i-1][1] - 不合法方案
// 对于 i == m 的情况,我们仅需减去一种方案,即全放的方案
// 对于 i > m 的情况,我们需要减去的是这样一种方案:i-m+1~i 共 m - 1 个位置都放,i-m 没有放。但是 i - m - 1 前的方案是不确定的,因此乘法原理处理。
f[0][0] = 1;
for (int i = 1; i < m; i++) f[i][1] = f[i][0] = f[i-1][0] + f[i-1][1];
for (int i = m; i <= n;) {
f[i][0] = f[i-1][0] + f[i-1][1];
f[i][1] = f[i-1][0] + f[i-1][1] - 1;
break;
}
for (int i = m + 1; i <= n; i++) {
f[i][0] = f[i-1][0] + f[i-1][1];
f[i][1] = f[i-1][0] + f[i-1][1] - f[i-1-m][1] - f[i-1-m][0];
}
cout << f[n][0] + f[n][1];
}
之后,可以发现其实我们用一维合并一下更好。
long long f[55];
int main() {
cin >> n >> m;
// 可以发现我们能够压缩一维
// f[i][1] + f[i][0] = 2*(f[i-1][0] + f[i-1][1]) - 不合法方案
f[0] = 1;
for (int i = 1; i <= n; i++) {
f[i] = 2 * f[i-1];
if (i == m) f[i] -= 1;
if (i > m) f[i] -= f[i-1-m];
}
cout << f[n];
}