牛客周赛86

yxy 发布于 2025-03-24 最后更新于 2025-03-25 307 次阅读


说明

此文章仅作者个人见解。原题链接https://ac.nowcoder.com/acm/contest/104637

A.小苯跑外卖

题目描述

  • 小苯发现自己平均跑一单外卖可以赚 xx 元,于是,他给自己定下了目标:每天至少需要赚够 yy 元。他想知道,自己每天至少需要跑几单,才能达成他定下的目标。请你帮他算一算吧。

思路

  • 语法题,向上取整即可。时间复杂O(1)。

代码

void solve()
{
   int x , y;
   cin >> x >> y;
   cout << (y % x == 0 ? y / x : (y / x) + 1) << endl;
}

B.小苯的区间删除

题目描述

  • 小苯有一个由 n 个整数组成的数组 {a_1, a_2, ..., a_n},他想要使得数组中的元素和最大。为此,小苯可以进行任意次以下操作(也可以不操作):
    选择一个长度不超过 k 的区间 [l, r] (1 ≤ l ≤ r ≤ n; r - l + 1 ≤ k),将 a_l, a_{l+1}, ..., a_r 这段元素删除,并将其余元素按照原有顺序前后拼接起来。换句话说,操作后数组变为:
    a_1, a_2, ..., a_{l-1}, a_{r+1}, ..., a_n。
    小苯想知道,在可以进行任意次上述操作的前提下,数组中的元素和最大可以变为多少。特别地,空数组的总和视为 0。

思路

  • 思维题,删除操作可有无数次,使得元素和最大。即保证所有的元素都为非负数即可。时间复杂度O(n)。

代码

void solve()
{
   int t;
   cin >> t;
   while (t -- )
   {
        int n , m;
        cin >> n >> m;
        vector<int> a(n + 1);
        for (int i = 1 ; i <= n ; i ++ ) cin >> a[i];

        int ans = 0;
        for (int i = 1 ; i <= n ; i ++ ) if (a[i] > 0) ans += a[i];

        cout << ans << endl;
   }

}

C.小苯的数字消除

题目描述

  • 小苯有一个长度为 n,仅由字符 ‘0’ 和 ‘1’ 构成的字符串 s。下标从 1 开始。
    小苯认为 s 的长度过长,他希望用一些“消除”操作,将 s 的长度变得尽可能短。一次“消除”操作描述为:
    如果存在这样的 i (1 ≤ i < |s|) 满足 s_i = s_{i+1},那么小苯可以将 s_i, s_{i+1} 这两个字符消除,其余的字符按原有顺序拼接起来。换句话说,一次操作过后的字符串变为 s = "s_1 s_2 ... s_{i-1} s_{i+2} ... s_{|s|}"(其中,|s| 用于表示字符串 s 的长度)。
    小苯为了最小化 s 的长度,因此在开始“消除”操作之前,他可以进行一些“修改”操作。一次“修改”操作描述为:
    选择任意一个位置上的字符,将其改为 ‘0’ 或 ‘1’ 中的任意一种。
    现在,小苯想知道,他至少需要先进行几次“修改”操作,才能使得存在这样一种“消除”操作的执行方案,使得 s 的长度不超过 1。请你帮他算一算吧。

思路

  • 贪心。使用栈先进后出的特点,将前面的数字存入,遇到相同的数字则退栈,否则入栈。最后会得到类似01010101的串,则操作次数为arr.size() / 2。时间复杂度O(n)。

代码

void solve()
{
    int t;
    cin >> t;
    while (t -- ) 
    {
        int n;
        cin >> n;
        string s;
        cin >> s;
        s = " " + s;
        stack<char> stc;
        
        for (int i = 1 ; i <= n ; i ++ )
        {
            if (stc.size() && stc.top() == s[i])
                stc.pop();
            else stc.push(s[i]);
        }
        cout << stc.size() / 2 << endl;
    }
}

D.小苯的数字集合

题目描述

  • 小苯有一个可重的数字集合 N,初始时,集合中恰好有两个正整数 x 和 y。
    现在,小苯可以执行若干轮操作,每一轮操作他都会从 N 中选择一个数字 a 和另一个数字 b,并从下述四种中选择一种进行:
    ∙ 将 a 与 b 进行位与运算(and)的结果加入集合。
    ∙ 将 a 与 b 进行位或运算(or)的结果加入集合。
    ∙ 将 a 与 b 进行位异或运算(xor)的结果加入集合。
    ∙ 将 a 与 b 的最大公约数(gcd)加入集合。
    小苯想知道,最少需要经过多少轮操作,才能使得自己的数字集合中出现数字 0,请你帮他算一算吧。
    如果您需要更多位运算相关的知识,可以参考 OI-Wiki 的相关章节。相关链接:https://oi-wiki.org/math/bit/#%E4%B8%8E%E6%88%96%E5%BC%82%E6%88%96
    最大公约数,指两个整数共有约数中最大的一个。例如,12 和 30 的公约数有 1, 2, 3, 6,其中最大的约数是 6,因此 gcd(12, 30) = 6。

思路

  • (bfs暴力)可以证明答案不会超过3。因为是可重集合,每次操作后都会加入集合当前操作后的元素,由 a ^ b ^ b = a 可知 最少两次操作之后会出现相同的数字,而 a ^ a = 0。所以可知最多三次操作之后就可以得到0。时间复杂度O(n²)。

代码

void solve()
{
    int t;
    cin >> t;
    while (t -- )
    {
        int x , y;
        cin >> x >> y;
        queue<pair<pair<int , int> , int> > q;
        q.push({{x , y} , 0});
        vector<int> vec{x , y};
        while (q.size())
        {
            auto &[a , cnt] = q.front();
            q.pop();
            
            x = a.first , y = a.second;
            
            if (!(x ^ y) || !(x & y) || !(x | y))
            {
                cout << cnt + 1 << endl;
                break;
            }
            
            int g = gcd(x , y);
            int arr[4] = {x ^ y , x & y , x | y , g};
                
            for (auto i : vec)
                for (auto j : arr)
                    q.push({{i , j} , cnt + 1});
            for (auto i : arr)
                vec.push_back(i);
        }
    }
}