牛客小白月赛99(下)
%%%
题目描述
登录—专业IT笔试面试备考平台_牛客网
运行代码
#include <iostream>
#include <cmath>
using namespace std;
long long maxO(long long n) {if (n == 0) return 0;return static_cast<long long>(floor(log2(n+1 )));
}
int main() {int T;cin >> T;while (T--) {long long n;cin >> n;cout << maxO(n) << endl;}return 0;
}
代码思路
一、整体思路
- 首先从输入中获取测试数据的组数
T
。 - 对于每组测试数据,读取一个整数
n
。 - 调用
maxO
函数计算将n
变为0
所需的最多操作次数,并输出结果。
二、具体原理
-
maxO
函数部分- 如果输入的整数
n
为0
,直接返回0
,因为不需要任何操作就已经是0
了。 - 对于非零的整数
n
,利用数学推导得出将n
变为0
最多需要的操作次数为floor(log2(n + 1))
数学推导过程如下:
- 当
n = 1
时,只需要一次操作(1 % 1 = 0
)。 - 当
n = 2
时,需要两次操作(2 % 2 = 0
)。 - 当
n = 3
时,需要两次操作(3 % 2 = 1
,1 % 1 = 0
)。 - 当
n = 4
时,需要三次操作(4 % 3 = 1
,1 % 1 = 0
)。 - 观察可得,对于一个整数
n
,将其变为0
的过程可以看作是不断找到一个小于等于n
的最大数mod
,使得n % mod
尽可能小,然后重复这个过程直到n
变为0
。而每次操作都使得n
至少减半(当mod
取尽可能接近n
的数时),所以可以得出将n
变为0
最多需要的操作次数与以2
为底n + 1
的对数相关。
- 如果输入的整数
-
主函数部分
- 读取测试数据组数
T
。 - 在循环中,每次读取一个整数
n
,调用maxO
函数计算操作次数并输出。
- 读取测试数据组数
通过这种方式,程序可以快速计算出将给定整数 n
变为 0
所需的最多操作次数。
迷宫
题目描述
登录—专业IT笔试面试备考平台_牛客网
运行代码
#include <iostream>
#include <queue>
#include <string>
#define ll long long
using namespace std;
string s[1005];
ll n, m, f = 0;
ll sx, sy, ex, ey;
int xx[] = {0, -1, 0, 1};
int yy[] = {1, 0, -1, 0};
int zz[] = {1, 0, -1};
int hang[1005];
int lie[1005];void dfs(ll x, ll y) {queue<pair<ll, ll>> q;q.push({x, y});while (!q.empty()) {auto t = q.front();q.pop();if (hang[t.first] == 1 || lie[t.second] == 1) {f = 1;return;}for (int i = 0; i < 4; i++) {int dx = t.first + xx[i];int dy = t.second + yy[i];if (dx < 1 || dx > n || dy < 1 || dy > m || s[dx][dy] == '#') continue;s[dx][dy] = '#';q.push({dx, dy});}}
}void bfs(ll x, ll y) {for (int i = 0; i < 3; i++) {hang[x + zz[i]] = 1;lie[y + zz[i]] = 1;}s[ex][ey] = '@';queue<pair<ll, ll>> q;q.push({x, y});while (!q.empty()) {auto t = q.front();q.pop();for (int i = 0; i < 4; i++) {int dx = t.first + xx[i];int dy = t.second + yy[i];if (dx < 1 || dx > n || dy < 1 || dy > m || s[dx][dy]!= '.') continue;s[dx][dy] = '@';q.push({dx, dy});for (int j = 0; j < 3; j++) {hang[dx + zz[j]] = 1;lie[dy + zz[j]] = 1;}}}
}void solve() {cin >> n >> m;for (int i = 1; i <= n; i++) {cin >> s[i];s[i] = " " + s[i];}for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {if (s[i][j] == 'S') sx = i, sy = j;if (s[i][j] == 'E') ex = i, ey = j;}}s[sx][sy] = '#';bfs(ex, ey);dfs(sx, sy);if (f == 1)cout << "YES\n";elsecout << "NO\n";
}int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int T = 1;while (T--) {solve();}return 0;
}
代码思路
一、整体思路
- 首先从输入中获取迷宫的行数
n
和列数m
,以及迷宫的布局描述。 - 找到迷宫中的起点
S
和终点E
的位置。 - 先从终点开始进行广度优先搜索(
bfs
),标记出可以通过一次超能力清理障碍物后能到达的位置。 - 再从起点开始进行深度优先搜索(
dfs
),在搜索过程中判断是否能够到达标记过的位置,如果能到达则说明可以利用超能力从起点到达终点。
二、具体原理
-
输入数据处理部分
- 使用
cin >> n >> m
读取迷宫的行数和列数。 - 然后使用两个循环读取迷宫的布局,将每一行的字符串存储在
s
数组中,并在字符串开头添加一个空格以便后续处理时避免边界问题。 - 在读取过程中找到起点
S
和终点E
的位置,分别存储在sx
、sy
、ex
、ey
变量中。
- 使用
-
bfs
函数部分- 这个函数从终点开始进行广度优先搜索,用于标记可以通过一次超能力清理障碍物后能到达的位置。
- 首先遍历三个方向(上、下、左、右中的三个),将对应位置的标记数组
hang
和lie
设为1
,表示这些位置可以通过超能力到达。 - 将终点
E
的字符设为@
,表示已经访问过。 - 使用队列进行广度优先搜索,从终点开始向四个方向扩展,如果扩展到的位置是
.
(空地),则将其设为@
,并将该位置加入队列,同时再次遍历三个方向更新标记数组。
-
dfs
函数部分- 这个函数从起点开始进行深度优先搜索。
- 使用队列存储待搜索的位置。
- 在搜索过程中,如果当前位置的行或列被标记为可以通过超能力到达(即
hang[t.first] == 1 || lie[t.second] == 1
),则说明可以利用超能力到达终点,将标志变量f
设为1
并返回。 - 否则,向四个方向扩展搜索,如果扩展到的位置是空地且不在队列中,则将其设为
#
(表示已访问过)并加入队列。
-
solve
函数部分- 主要的处理函数,首先读取输入数据,找到起点和终点位置,然后调用
bfs
和dfs
函数进行搜索,最后根据标志变量f
的值输出结果。
- 主要的处理函数,首先读取输入数据,找到起点和终点位置,然后调用
-
主函数部分
- 设置输入输出同步和时间限制等。
- 调用
solve
函数进行处理。
通过这种方式,程序可以判断在给定的迷宫中是否能够利用超能力从起点到达终点。
又是一年毕业季
题目描述
登录—专业IT笔试面试备考平台_牛客网
运行代码
#include <iostream>
#include <vector>
#include <map>const int MAXN = 3000005;
std::vector<int> primes;
std::map<int, int> numbers;void findPrimes() {std::vector<bool> isPrime(MAXN, true);for (int i = 2; i < MAXN; ++i) {if (isPrime[i]) {primes.push_back(i);for (int j = i * i; j < MAXN; j += i) {isPrime[j] = false;}}}
}void solve() {int n;std::cin >> n;numbers.clear();for (int i = 1; i <= n; ++i) {int num;std::cin >> num;numbers[num] = 1;}for (const int& prime : primes) {if (!numbers[prime]) {std::cout << prime << '\n';return;}}
}int main() {findPrimes();int T = 1;std::cin >> T;while (T--) {solve();}return 0;
}
代码思路
一、整体思路
- 首先定义一个足够大的整数数组范围
MAXN
,创建一个存储素数的向量primes
和一个映射numbers
来存储输入的每个人两次眨眼的间隔时间。 - 定义一个函数
findPrimes
来预先计算并存储一定范围内的所有素数。 - 定义
solve
函数来处理每组测试数据,读取输入的人数和每个人的眨眼间隔时间,存储在numbers
映射中。然后遍历预先计算好的素数列表,找到第一个不在输入的眨眼间隔时间中的素数,输出该素数作为最小等待时间。 - 在主函数中,先调用
findPrimes
计算素数,然后读取测试数据组数T
,并在循环中调用solve
函数处理每组测试数据。
二、具体原理
-
findPrimes
函数部分- 使用埃拉托斯特尼筛法计算并存储一定范围内的所有素数。
- 创建一个布尔数组
isPrime
初始化为true
,表示对应的索引值是否为素数。 - 从
2
开始遍历到MAXN
,如果当前索引值对应的isPrime
值为true
,说明是素数,将其加入primes
向量中,然后遍历该素数的倍数,将它们在isPrime
数组中标记为false
,表示不是素数。
-
solve
函数部分- 读取输入的人数
n
,然后遍历n
次,读取每个人的眨眼间隔时间num
,并将其存储在numbers
映射中,键为眨眼间隔时间,值为1
(这里的值并不重要,只是用于标记该眨眼间隔时间已经出现过)。 - 遍历预先计算好的素数列表
primes
,如果当前素数不在numbers
映射中,说明这个素数不是任何人的眨眼间隔时间,输出该素数作为最小等待时间,并返回。
- 读取输入的人数
-
主函数部分
- 首先调用
findPrimes
函数计算素数。 - 读取测试数据组数
T
。 - 在循环中调用
solve
函数处理每组测试数据。
- 首先调用
这个算法的原理是基于这样的假设:如果一个时间点不是任何人的眨眼时间,那么在这个时间点拍照可以确保每个人的眼睛都处于睁开状态。而通过寻找第一个不在输入的眨眼间隔时间集合中的素数,可以快速确定这样的时间点。由于素数的性质,它不能被其他小于它的数整除,所以不太可能与输入的眨眼间隔时间重合,除非这个素数本身就是某个学生的眨眼间隔时间。