牛客小白月赛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;
}

代码思路

一、整体思路

  1. 首先从输入中获取测试数据的组数 T
  2. 对于每组测试数据,读取一个整数 n
  3. 调用 maxO 函数计算将 n变为 0所需的最多操作次数,并输出结果。

二、具体原理

  1. 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 = 11 % 1 = 0)。
    • 当 n = 4 时,需要三次操作(4 % 3 = 11 % 1 = 0)。
    • 观察可得,对于一个整数 n,将其变为 0的过程可以看作是不断找到一个小于等于 n的最大数 mod,使得 n % mod尽可能小,然后重复这个过程直到 n变为 0。而每次操作都使得 n至少减半(当 mod取尽可能接近 n的数时),所以可以得出将 n变为 0最多需要的操作次数与以 2为底 n + 1的对数相关。
  2. 主函数部分

    • 读取测试数据组数 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";
    else
        cout << "NO\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T = 1;
    while (T--) {
        solve();
    }
    return 0;
}

代码思路

一、整体思路

  1. 首先从输入中获取迷宫的行数 n 和列数 m,以及迷宫的布局描述。
  2. 找到迷宫中的起点 S 和终点 E 的位置。
  3. 先从终点开始进行广度优先搜索(bfs),标记出可以通过一次超能力清理障碍物后能到达的位置。
  4. 再从起点开始进行深度优先搜索(dfs),在搜索过程中判断是否能够到达标记过的位置,如果能到达则说明可以利用超能力从起点到达终点。

二、具体原理

  1. 输入数据处理部分

    • 使用 cin >> n >> m 读取迷宫的行数和列数。
    • 然后使用两个循环读取迷宫的布局,将每一行的字符串存储在 s 数组中,并在字符串开头添加一个空格以便后续处理时避免边界问题。
    • 在读取过程中找到起点 S 和终点 E 的位置,分别存储在 sxsyexey 变量中。
  2. bfs 函数部分

    • 这个函数从终点开始进行广度优先搜索,用于标记可以通过一次超能力清理障碍物后能到达的位置。
    • 首先遍历三个方向(上、下、左、右中的三个),将对应位置的标记数组 hang 和 lie 设为 1,表示这些位置可以通过超能力到达。
    • 将终点 E 的字符设为 @,表示已经访问过。
    • 使用队列进行广度优先搜索,从终点开始向四个方向扩展,如果扩展到的位置是 .(空地),则将其设为 @,并将该位置加入队列,同时再次遍历三个方向更新标记数组。
  3. dfs 函数部分

    • 这个函数从起点开始进行深度优先搜索。
    • 使用队列存储待搜索的位置。
    • 在搜索过程中,如果当前位置的行或列被标记为可以通过超能力到达(即 hang[t.first] == 1 || lie[t.second] == 1),则说明可以利用超能力到达终点,将标志变量 f 设为 1并返回。
    • 否则,向四个方向扩展搜索,如果扩展到的位置是空地且不在队列中,则将其设为 #(表示已访问过)并加入队列。
  4. solve 函数部分

    • 主要的处理函数,首先读取输入数据,找到起点和终点位置,然后调用 bfs 和 dfs 函数进行搜索,最后根据标志变量 f 的值输出结果。
  5. 主函数部分

    • 设置输入输出同步和时间限制等。
    • 调用 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;
}

代码思路

一、整体思路

  1. 首先定义一个足够大的整数数组范围 MAXN,创建一个存储素数的向量 primes 和一个映射 numbers 来存储输入的每个人两次眨眼的间隔时间。
  2. 定义一个函数 findPrimes 来预先计算并存储一定范围内的所有素数。
  3. 定义 solve 函数来处理每组测试数据,读取输入的人数和每个人的眨眼间隔时间,存储在 numbers 映射中。然后遍历预先计算好的素数列表,找到第一个不在输入的眨眼间隔时间中的素数,输出该素数作为最小等待时间。
  4. 在主函数中,先调用 findPrimes 计算素数,然后读取测试数据组数 T,并在循环中调用 solve 函数处理每组测试数据。

二、具体原理

  1. findPrimes 函数部分

    • 使用埃拉托斯特尼筛法计算并存储一定范围内的所有素数。
    • 创建一个布尔数组 isPrime 初始化为 true,表示对应的索引值是否为素数。
    • 从 2 开始遍历到 MAXN,如果当前索引值对应的 isPrime 值为 true,说明是素数,将其加入 primes 向量中,然后遍历该素数的倍数,将它们在 isPrime 数组中标记为 false,表示不是素数。
  2. solve 函数部分

    • 读取输入的人数 n,然后遍历 n 次,读取每个人的眨眼间隔时间 num,并将其存储在 numbers 映射中,键为眨眼间隔时间,值为 1(这里的值并不重要,只是用于标记该眨眼间隔时间已经出现过)。
    • 遍历预先计算好的素数列表 primes,如果当前素数不在 numbers 映射中,说明这个素数不是任何人的眨眼间隔时间,输出该素数作为最小等待时间,并返回。
  3. 主函数部分

    • 首先调用 findPrimes 函数计算素数。
    • 读取测试数据组数 T
    • 在循环中调用 solve 函数处理每组测试数据。

这个算法的原理是基于这样的假设:如果一个时间点不是任何人的眨眼时间,那么在这个时间点拍照可以确保每个人的眼睛都处于睁开状态。而通过寻找第一个不在输入的眨眼间隔时间集合中的素数,可以快速确定这样的时间点。由于素数的性质,它不能被其他小于它的数整除,所以不太可能与输入的眨眼间隔时间重合,除非这个素数本身就是某个学生的眨眼间隔时间。

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

筱姌

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

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

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

打赏作者

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

抵扣说明:

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

余额充值