牛客网50题

发布于:2025-07-13 ⋅ 阅读:(16) ⋅ 点赞:(0)

12.游游的整数切割
C++实现:
游游拿到了一个正整数,她希望将它切割成两部分,使得它们的和为偶数。游游想知道有多少种合法的切割方案?
注:切割后的正整数允许出现前导零。
输入描述:
一个正整数,大小不超过1e100000
输出描述:
一个整数,代表切割的方案数。

示例1

输入:
103
输出:
1

说明
切割成1+03=4是合法的,但10+3=13为奇数,不符合要求。所以有1种合法方案。

算法设计

​​输入处理​​:读取字符串形式的大整数。
​​特殊情况​​:若字符串长度=1,无法切割,直接返回 0。
​​固定右半部分奇偶性​​:取字符串最后一个字符 last_char。
​​遍历切割点​​:对每个可能的切割位置 i(从位置 1 到 n-1):
    左半部分最后一位是 s[i-1]
    比较 s[i-1] 和 last_char 的奇偶性(通过 ASCII 值直接取模 2)
    若奇偶性相同,则方案有效,计数器加 1。
​​输出结果​​:返回有效方案的总数。
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    cin >> s;
    int n = s.size();

    // 无法切割的情况
    if (n == 1) {
        cout << 0 << endl;
        return 0;
    }

    char last_char = s[n - 1];
    long long count = 0;

    // 遍历所有切割位置(左半部分长度从 1 到 n-1)
    for (int i = 1; i < n; i++) {
        // 比较左半部分最后一位与整个数字最后一位的奇偶性
        if ((s[i - 1] % 2) == (last_char % 2)) {
            count++;
        }
    }

    cout << count << endl;
    return 0;
}

13.小红的数位操作
C++实现:
小红拿到了一个正整数n,她可以进行若干次操作,每次操作将选择一个数位,使其加1或者减1.
不过有两条限制:
1.每个数位最多只能操作一次。
2.如果选择的是9,则无法进行加1操作。如果选择的是0,则无法进行减1操作。

小红希望最终n成为p的倍数,你能帮小红输出操作结束后的整数n吗?

输入描述:
两个正整数n和p。
1<=n,p<=1e13

输出描述:
如果无解,请输出-1。
否则输出一个整数(如果操作后包含了前导零,请将前导零一起输出),代表操作结束后的整数。有多解时输出任意即可。

示例1:
输入
72 7
输出
63

示例2:
输入
101 123456789
输出
000

示例3:
输入
40 9
输出
-1

代码解释

​​输入处理​​:读取整数n和p,并将n转换为字符串s以便逐位处理。
​​状态枚举​​:计算总状态数total = 3^len,每个状态表示一个操作组合(三进制表示)。
​​操作分解​​:对于每个状态,分解为操作数组op,其中op[i]表示第i位的操作(0-不操作,1-加1,2-减1)。
​​合法性检查​​:遍历每个数位,检查操作是否合法(0不能减1,9不能加1,操作后数字必须在0~9之间)。
​​模值计算​​:对合法操作,逐位计算新数字的模p值。
​​解输出​​:若当前状态操作后数字模p为0,则输出结果并结束;若所有状态枚举完毕无解,则输出-1。

此方法高效枚举所有可能的操作组合,通过逐位验证和模运算,确保在合理时间内找到解(若存在)。时间复杂度为O(3^len * len),适用于位数不超过14的整数。

#include <iostream>              // 引入输入输出流头文件
#include <string>                // 引入字符串处理头文件
#include <vector>                // 引入vector容器头文件
using namespace std;

int main() {
    long long n, p;
    cin >> n >> p;               // 读取输入的正整数n和p
    string s = to_string(n);     // 将n转为字符串,便于逐位操作
    int len = s.length();        // 获取n的位数
    long long total = 1;
    for (int i = 0; i < len; i++) {
        total *= 3;              // 计算所有操作方案总数(每位有3种操作:不变、+1、-1)
    }

    // 枚举所有可能的操作方案
    for (long long state = 0; state < total; state++) {
        vector<int> op(len);     // 存储每一位的操作类型
        long long tmp = state;
        // 将state转为三进制,表示每一位的操作
        for (int i = len-1; i >= 0; i--) {
            op[i] = tmp % 3;     // 0: 不变, 1: +1, 2: -1
            tmp /= 3;
        }

        long long mod_val = 0;   // 存储当前操作后n对p取模的值
        string ans_str;          // 存储操作后的结果字符串
        bool valid = true;       // 标记当前方案是否合法

        // 对每一位进行操作
        for (int i = 0; i < len; i++) {
            int d = s[i] - '0';  // 原始数位
            int new_d = d;
            if (op[i] == 1) {
                new_d = d + 1;   // +1操作
            } else if (op[i] == 2) {
                new_d = d - 1;   // -1操作
            }

            // 检查操作是否合法
            if (d == 0 && op[i] == 2) { // 0不能-1
                valid = false;
                break;
            }
            if (d == 9 && op[i] == 1) { // 9不能+1
                valid = false;
                break;
            }
            if (new_d < 0 || new_d > 9) { // 新数位必须在0~9
                valid = false;
                break;
            }
            ans_str += ('0' + new_d);     // 拼接新数位到结果字符串
            mod_val = (mod_val * 10 + new_d) % p; // 动态计算新数对p的模
        }

        if (!valid) continue;    // 如果方案不合法,跳过
        if (mod_val == 0) {      // 如果操作后n是p的倍数
            cout << ans_str << endl; // 输出结果(保留前导零)
            return 0;            // 结束程序
        }
    }

    cout << -1 << endl;          // 如果没有合法方案,输出-1
    return 0;
}

14.小美走公路
C++实现:
有一个环形的公路,上面共有n站,现在给定了顺时针第i站到第i+1站之间的距离(特殊的,也给出了第n站到第1站的距离)。小美想沿着公路第x站走到第y站,她想知道最短的距离是多少?

输入描述:
第一行输入一个正整数n,代表站的数量。
第二行输入n个正整数ai,前n-1个数代表顺时针沿着公路走,i站到第i+1站之间的距离;最后一个正整数代表顺时针沿着公路走,第n站到第1站的距离。
第三行输入两个正整数x和y,代表小美的出发地和目的地。
1<=n <= 1e5
1<=ai <= 1e9
1<=x,y <=n

输出描述:
一个正整数,代表小美走的最短距离。

示例1
输入
3
1 2 2
2 3

输出
2

示例2
输入
3
1 2 2
1 3
输出
2

#include <iostream>                  // 引入输入输出流头文件
#include <vector>                    // 引入vector容器头文件
#include <algorithm>                 // 引入算法头文件(用于swap和min)
using namespace std;

int main() {
    int n;
    cin >> n;                        // 读取站点数量
    vector<long long> dist(n);       // 存储每段距离
    vector<long long> prefix(n + 1, 0); // 前缀和数组,prefix[i]表示前i段距离之和
    long long total = 0;             // 公路总长度

    // 读取每段距离并计算前缀和
    for (int i = 0; i < n; i++) {
        cin >> dist[i];              // 读取第i段距离
        total += dist[i];            // 累加到总长度
        prefix[i + 1] = prefix[i] + dist[i]; // 计算前缀和
    }

    int x, y;
    cin >> x >> y;                   // 读取起点和终点站编号

    // 转换为0-based索引
    x--;
    y--;

    // 确保x <= y,方便后续计算顺时针距离
    if (x > y) {
        swap(x, y);
    }

    // 计算顺时针距离:从x到y的距离为prefix[y] - prefix[x]
    long long clockwise = prefix[y] - prefix[x];
    // 逆时针距离 = 总长度 - 顺时针距离
    long long counter_clockwise = total - clockwise;

    // 输出最短距离
    cout << min(clockwise, counter_clockwise) << endl;

    return 0;
}

15.小美的排列询问
C++实现:
小美拿到了一个排列。她想知道在这个排列种,x和y是否是相邻的。
排列是指一个长度为n 的数组,其中1到n每个元素恰好出现一次。
输入描述:
第一行输入一个正整数n,代表排列的长度。
第二行输入n个正整数ai,代表排列的元素。
第三行输入两个正整数x和y,用空格隔开。
1<=n<=200000
1<=ai, x,y<=n
保证x!=y
输出描述:
如果x和y在排列中相邻,则输出"Yes"。否则输出“No”。

示例1
输入
4
1 4 2 3
2 4
输出
Yes

示例2
输入
5
3 4 5 1 2
3 2
输出
No

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    vector<int> pos(n + 1); // 记录每个数的位置
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        pos[a[i]] = i; // 记录值为a[i]的元素在第i个位置
    }
    int x, y;
    cin >> x >> y;
    // 检查x和y的位置是否相邻
    if (abs(pos[x] - pos[y]) == 1) {
        cout << "Yes" << endl;
    } else {
        cout << "No" << endl;
    }
    return 0;
}

16.函数
C++实现:
对于一个十进制正整数x,如果x的每一位数字只可能是1,2,3中的其中一个,则称x是完美数。如:
123,1,3321都是完美数,而5,1234则不是。
牛牛想写一个函数f(n),使得其返回最大的不大于n的完美数,请你帮助牛牛实现这个函数。

输入描述:
第一行一个正整数T表示单组测试数据的组数。
接下来T行每行一个正整数n。
(1<=T<=1e5, 1<=n<=1e18)

输出描述:
对于每组输入的n,输出f(n)的值。

示例1
输入:
4
213
3244
22
100

输出
213
3233
22
33

说明
如题所述:f(213)=213,f(3244)=3233,f(22)=22, f(100) = 33。

功能解释:
该程序用于求最大的不大于n的“完美数”,即每一位都是1、2、3的数。

对于每个输入的n,从高位到低位找到第一个不是1/2/3的数位,将其替换为比它小的最大1/2/3,其后全部置为3。
如果当前位无法替换,则向前借位,前面一位减到最近的1/2/3,其后全部置为3。
若n本身就是完美数,直接输出n。
支持超大整数输入(用字符串处理)。

#include <iostream>
#include <string>
using namespace std;

bool dfs(int pos, bool free, const string& s, string& res) {
    int n = s.size();
    if (pos == n) {
        return true;
    }
    if (free) {
        res.append(n - pos, '3');
        return true;
    }
    int current_digit = s[pos] - '0';
    for (int d = 3; d >= 1; d--) {
        if (d > current_digit) continue;
        bool next_free = (d < current_digit);
        int old_size = res.size();
        res.push_back('0' + d);
        if (next_free) {
            res.append(n - pos - 1, '3');
            return true;
        } else {
            if (dfs(pos + 1, false, s, res)) {
                return true;
            }
            res.resize(old_size);
        }
    }
    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while (T--) {
        string n;
        cin >> n;
        string res;
        bool success = dfs(0, false, n, res);
        if (!success) {
            if (n.size() > 1) {
                res = string(n.size() - 1, '3');
            } else {
                res = "0";
            }
        }
        cout << res << '\n';
    }
    return 0;
}
  1. 子序列
    C++实现
    在数学中,某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置而形成的新序列,如对于字符串"abc","ab"和"ac"都是其子序列,而"cb"和"ca"不是。
    牛牛有一个长度为n的仅由小写字母组成的字符串s,牛牛想知道s有多少子序列恰好包含k种字母?
    输入描述:
    第一行输入两个正整数n和k。
    第二行输入一个长度为n的仅包含小写字母的字符串s。
    (1<=n<=1e5, 1<=k<=26)

输出描述
由于答案可能会很大,因此你只需要输出子序列个数对1e9 + 7取模的结果即可。

示例1
输入
6 5
eecbad
输出
3
说明
显然s有两个子序列“ecbad”满足要求,同时s自己也满足要求,因此答案为3。

示例2
输入
10 2
aaccebecd
输出
126

GitHub Copilot
功能解释:
该程序用于统计字符串s中,恰好包含k种不同字母的子序列个数(对1e9+7取模)。

统计每个字母出现次数,预处理2的幂次。
用DFS枚举所有k种字母的组合,每种组合的方案数为每种字母选至少1个(2^cnt[i]-1),乘积即为该组合的子序列数。
答案为所有组合的方案数之和。


#include <iostream>                  // 引入输入输出流头文件
#include <vector>                    // 引入vector容器头文件
#include <string>                    // 引入字符串处理头文件
using namespace std;

const long long MOD = 1000000007;    // 取模常数

// 深度优先搜索,枚举所有k种字母的组合
void dfs(int start, int k, int count, const vector<long long>& power2, const vector<int>& cnt, vector<int>& current, long long& ans) {
    if (count == k) {                // 如果已经选了k种字母
        long long temp = 1;
        for (int idx : current) {    // 对于当前组合的每个字母
            long long term = power2[cnt[idx]] - 1; // 该字母可以选至少1个,方案数为2^cnt[idx]-1
            temp = (temp * term) % MOD;            // 累乘所有字母的方案数
        }
        ans = (ans + temp) % MOD;    // 累加到答案
        return;
    }
    if (start >= 26)                 // 超过26个字母,返回
        return;
    
    for (int i = start; i < 26; i++) { // 枚举从start到25的所有字母
        current.push_back(i);        // 选择第i个字母
        dfs(i + 1, k, count + 1, power2, cnt, current, ans); // 递归选择下一个字母
        current.pop_back();          // 回溯,撤销选择
    }
}

int main() {
    ios::sync_with_stdio(false);     // 提高cin/cout效率
    cin.tie(0);                      // 解除cin和cout的绑定
    int n, k;
    cin >> n >> k;                   // 读取字符串长度n和要求的种类数k
    string s;
    cin >> s;                        // 读取字符串s

    vector<long long> power2(n + 1); // 预处理2的幂次
    power2[0] = 1;
    for (int i = 1; i <= n; i++) {
        power2[i] = (power2[i - 1] * 2) % MOD; // power2[i] = 2^i % MOD
    }

    vector<int> cnt(26, 0);          // 统计每个字母出现次数
    for (char c : s) {
        cnt[c - 'a']++;
    }

    long long ans = 0;               // 记录答案
    vector<int> current;             // 当前组合的字母编号

    dfs(0, k, 0, power2, cnt, current, ans); // 从第0个字母开始枚举k种字母的组合

    if (ans < 0)                     // 防止负数,补正
        ans += MOD;
    cout << ans << '\n';             // 输出答案

    return 0;
}

18.散落的金币
C++实现
金币散落在长度为N米的道路上,且每隔一米就会有一堆金币。道路的始端编号为1,末端编号为N。起初在编号为K处,开始收集金币。最后也要回到编号K处。操作:
每次在一个点只拿一枚金币,初始时可以不拿金币。
留在当前位置或者移动到相邻位置点取金币(即移动时每次只能移动一米),但是他不会移动到没有金币的地方更不会在没有金币的地方停留(如果该处没有金币了,就无法往这个点的方向进行了。即不能跳过该点继续进行)。
编写一个程序,计算最多可以获得多少金币。

输入描述:
第一行给出两个正整数N和K
第二行给出N个正整数Ai,表示编号为i处的地方有Ai枚金币。用空格间隔。
1<=N<=100000
1<=K<=N
0<=Ai<=1e9

输出描述
输出最多可以获得多少枚金币

示例1
输入
4 1
3 2 2 1
输出
8
说明路径可以为1->2->3->4->3->2->1->1->1

示例2
输入
5 3
2 1 0 1 2
输出
0
说明
如果从点3移动到相邻的另一个点,最终不会回到点3(因为点3没有金币),所以不能移动。

示例3
输入
5 3
3 1 3 1 3
输出
5
说明
3->2->3->4->3->3

功能解释:
该程序用于计算在规则允许下,最多可以获得多少金币。

只能在包含起点的最大连续有金币区间内移动和取金币,最终回到起点。
统计该区间内所有金币总数即为答案。

#include <iostream>                  // 引入输入输出流头文件
#include <vector>                    // 引入vector容器头文件
using namespace std;

int main() {
    int N, K;
    cin >> N >> K;                   // 读取道路长度N和起点编号K
    vector<long long> A(N);          // 存储每个位置的金币数量
    for (int i = 0; i < N; ++i) cin >> A[i]; // 读取每个位置的金币数
    --K; // 转为0-based索引,方便数组操作

    // 如果起点没有金币,无法移动,直接输出0
    if (A[K] == 0) {
        cout << 0 << endl;
        return 0;
    }

    // 计算向左最多能走到哪里(连续有金币的最左端)
    int left = K;
    while (left > 0 && A[left - 1] > 0) left--;

    // 计算向右最多能走到哪里(连续有金币的最右端)
    int right = K;
    while (right < N - 1 && A[right + 1] > 0) right++;

    // 统计[left, right]区间内所有金币总数
    long long ans = 0;
    for (int i = left; i <= right; ++i) {
        ans += A[i];
    }
    cout << ans << endl;             // 输出最多可以获得的金币数
    return 0;
}

19.相遇
C++实现并逐行注释
街道可以看着一维数轴,街边共有n个行人,第i个人在一维道路上的初始位置为xi,并有一个初始朝向ai:
若ai=0,则代表第i个人向左行走;
若ai=1,则代表第i个人向右行走。

保证所有行人的行走速度相同,且不存在两个人的初始位置重合。
在经过足够长的时间后,若在某一时刻两个人的位置重合,则称他们发生了一次相遇。
计算总共有多少对行人会相遇。

输入描述:
第一行输入一个整数n(1<=n<=1e5),表示行人数目。
此后n行,第i行输入两个整数xi,ai(0<=xi<=1e9;0<=ai<=1),分别表示第i个人的初始位置和行走朝向。
除此之外,保证xi互不相同。

输出描述:
输出一个整数,表示会发生相遇的行人对数。

示例1
输入
3
1 1
2 0
3 1
输出
1
说明
在这个样例中,有且只有第一个人和第二个人会相遇

示例2
输入
5
23 1
3 1
1 0
45 0
66 1
输出
2

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int n;
    cin >> n; // 读取行人数目
    vector<pair<int, int>> people(n); // 存储每个人的位置和朝向
    for (int i = 0; i < n; ++i) {
        cin >> people[i].first >> people[i].second; // 读取每个人的位置和朝向
    }

    // 按照位置从小到大排序,方便后续处理
    sort(people.begin(), people.end());

    int right_count = 0; // 记录到当前位置为止,向右走的人数
    int ans = 0;         // 记录相遇对数

    // 遍历所有人,按位置从左到右
    for (int i = 0; i < n; ++i) {
        int pos = people[i].first;
        int dir = people[i].second;
        if (dir == 1) {
            // 如果当前人向右走,统计数量
            right_count++;
        } else {
            // 如果当前人向左走,与之前所有向右走的人都会相遇
            ans += right_count;
        }
    }

    cout << ans << endl; // 输出相遇对数
    return 0;
}

20.小红的red字符串
C++实现并逐行注释:
小红拿到了一个只包含’r’,‘e’,‘d’三种字母的字符串。她想知道,有多少子串满足’r’,‘e’,'d’三种字母的数量严格相等?

输入描述
一个仅包含r’,‘e’,'d’三种字母的字符串。长度不超过200000。

输出描述
三种字母的数量相等的子串个数。

示例1
输入
redrde
输出
4

说明
共有以下四个合法子串
[red] rde
r [edr] de
red [rde]
[redrde]


#include <iostream>
#include <map>
using namespace std;

int main() {
    string s;
    cin >> s;  // 输入字符串
    long long ans = 0;  // 存储最终答案
    // 使用map记录状态出现的次数,状态由两个差值组成:(r_count - e_count, r_count - d_count)
    map<pair<int, int>, int> stateMap;
    // 初始状态:在开始位置(0,0)出现一次
    stateMap[{0, 0}] = 1;
    
    int r = 0, e = 0, d = 0;  // 分别记录当前遇到的'r','e','d'字符数量
    
    // 遍历字符串中的每个字符
    for (char c : s) {
        // 根据当前字符更新计数器
        if (c == 'r') r++;
        else if (c == 'e') e++;
        else if (c == 'd') d++;
        
        // 计算当前状态:r与e的差值,r与d的差值
        pair<int, int> currentState = {r - e, r - d};
        
        // 如果之前出现过相同状态,则当前状态与之前状态之间的子串满足条件
        if (stateMap.find(currentState) != stateMap.end()) {
            ans += stateMap[currentState];  // 累加该状态之前出现的次数
        }
        
        // 更新当前状态的出现次数
        stateMap[currentState]++;
    }
    
    // 输出满足条件的子串数量
    cout << ans << endl;
    return 0;
}

21.小红的与运算
C++实现并逐行注释:
小红拿到了一个数组,她想在其中选择k个数,使得这k个数的按位与运算的值尽可能大。
输入描述:
第一行输入两个正整数n和k,用空格隔开。
第二行输入n个正整数ai,代表小红拿到的数组。
1<=k<=n<=100000
1<=ai<=1e9

输出描述:
选k个数,按位与 运算的最大值

示例1
输入
5 2
2 3 4 6 5

输出
4

说明
选择5和6,位与运算为4。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int n, k;
    cin >> n >> k;
    vector<int> a(n);
    
    // 读取数组
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    
    int ans = 0; // 最终结果
    
    // 从高位到低位遍历(30位足够覆盖1e9范围)
    for (int bit = 30; bit >= 0; bit--) {
        // 尝试设置当前位为1
        int candidate = ans | (1 << bit);
        int count = 0; // 满足条件的数字数量
        
        // 遍历数组统计满足条件的元素数量
        for (int i = 0; i < n; i++) {
            // 检查数字是否包含候选模式的所有位
            if ((a[i] & candidate) == candidate) {
                count++;
            }
        }
        
        // 如果有至少k个数满足条件,则更新结果
        if (count >= k) {
            ans = candidate;
        }
    }
    
    cout << ans << endl;
    return 0;
}

22.小红的v三元组
C++实现并逐行注释:
小红拿到了一个数组a1,a2,…an,她想知道有多少组(i,j,k)为“v三元组”:
第一个数和第三个数相等。且第一个数大于第二个数。
用数学语言描述:
1 <=i < j < k <=n
ai = ak 且 ai > aj
输入 描述:
第一行输入一个正整数n,代表数组的元素个数。
第二行输入n个正整数ai,代表小红拿到的数组。
3<= n <= 100000
0 <= ai <= 1e9
输出描述:
v三元组的数量

示例1
输入
6
3 1 3 4 3 4

输出
3

说明
(1,2,3)
(1,2,5)
(4,5,6)

#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;

int main() {
    int n;
    cin >> n; // 读取数组长度
    vector<int> a(n);
    for (int i = 0; i < n; ++i) cin >> a[i]; // 读取数组元素

    unordered_map<int, int> cnt; // 记录每个数在当前位置右侧出现的次数
    long long ans = 0;           // 记录v三元组数量

    // 预处理每个数的出现次数
    for (int i = 0; i < n; ++i) cnt[a[i]]++;

    // 枚举j的位置
    for (int j = 0; j < n; ++j) {
        cnt[a[j]]--; // 当前j不能作为k
        // 枚举i的位置(i < j),只需统计a[i] > a[j]且a[i] == a[k]的对数
        // 用map记录左侧每个数出现次数
        static unordered_map<int, int> left_cnt;
        for (auto& p : left_cnt) {
            int val = p.first;
            if (val > a[j]) {
                ans += (long long)p.second * cnt[val];
            }
        }
        left_cnt[a[j]]++; // 当前数加入左侧统计
    }

    cout << ans << endl; // 输出v三元组数量
    return 0;
}

23.Monica的树
C++实现并逐行注释:
Monica拿到了一棵有根树,根节点为1号节点。每个节点被染成红色或者蓝色。
假设第i个节点的权值ai定义为:从根节点到该节点的路径上,红色节点和蓝色节点的数量之差(红色节点数减去蓝色节点数后取绝对值)。请你帮Monica计算出所有节点的权值之和。

输入描述
第一行输入一个正整数n,代表节点的数量。
第二行输入一个长度为n的字符串,字符‘R’代表第i个节点被染成红色,字符’B’代表第i个节点被染成蓝色。
接下来的n-1行,每行输入两个正整数u和v,代表节点u和节点v有一条路径连接。
1<=n<=1e5

输出描述
所有节点的权值之和。

示例1
输入
5
RBRBB
2 5
1 5
4 1
3 5
输出
3

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int n;
vector<vector<int>> tree; // 邻接表存储树结构
string color;             // 节点颜色
long long ans = 0;        // 权值之和

// DFS遍历,cur为当前节点,parent为父节点,red为路径上红色节点数,blue为路径上蓝色节点数
void dfs(int cur, int parent, int red, int blue) {
    // 根据当前节点颜色更新红蓝数量
    if (color[cur] == 'R') red++;
    else blue++;
    // 当前节点权值为红蓝数量之差的绝对值
    ans += abs(red - blue);
    // 遍历所有子节点
    for (int v : tree[cur]) {
        if (v != parent) {
            dfs(v, cur, red, blue);
        }
    }
}

int main() {
    cin >> n;
    cin >> color; // 读取颜色字符串
    tree.resize(n);
    for (int i = 0; i < n - 1; ++i) {
        int u, v;
        cin >> u >> v;
        --u; --v; // 转为0-based索引
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
    dfs(0, -1, 0, 0); // 从根节点0开始DFS
    cout << ans << endl; // 输出所有节点权值之和
    return 0;
}

24.小欧的括号操作
C++实现并逐行注释:
小欧拿到了一个只包含字符’(‘和’)‘的字符串,两种操作:
1.用’(‘代替一对相邻的括号(“()”)。
2.用’)'代替一对相邻的括号(“()”)。
注意,只有相邻的括号字符才可以操作。
若干次以后,该字符串的最短长度是多少?

输入描述:
一个只包含’(‘和’)'两种字符的字符串。长度不超过2*1e5
输出描述:
一个正整数,代表若干次操作后,字符串的最短长度。

示例1
输入
0
输出
1

示例2
输入
)(
输出
2

算法说明:

​​核心思想​​:通过模拟括号匹配过程,计算最大可匹配括号对数
​​匹配规则​​:
    左括号直接入栈(depth++)
    右括号与最近的左括号匹配(depth-- 且 matched++)
​​最终长度​​:原始长度减去匹配对数
    每对匹配的括号可通过操作减少1个字符
    未匹配的括号会保留在最终字符串中

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    cin >> s;  // 输入括号字符串
    int depth = 0;  // 当前未匹配的左括号数量
    int matched = 0;  // 匹配的括号对数

    // 遍历字符串中的每个字符
    for (char c : s) {
        if (c == '(') {
            // 遇到左括号,增加深度
            depth++;
        } else if (c == ')') {
            // 遇到右括号且存在未匹配的左括号
            if (depth > 0) {
                depth--;  // 减少深度
                matched++;  // 增加匹配对数
            }
        }
        // 忽略非括号字符(根据题意不会出现)
    }

    // 最终长度 = 原始长度 - 匹配对数
    cout << s.size() - matched << endl;
    return 0;
}

25.小欧的选数乘积
C++实现并逐行注释:
小欧拿到了两个正整数x和y,她想操作使得x不小于y。每一轮操作:从给定的整数数组a中选定一个元素ai,将x乘以ai,并删掉a数组中所有的等于ai的数字。
进行多少轮,可以使得x不小于y?

输入描述:
第一行输入两个正整数x和y,用空格隔开。
第二行输入一个正整数n,代表数组大小。
第三行输入n个正整数ai,代表数组的元素。
1<=x,y,ai<=1e9
1<=n<=1e5

输出描述:
如果小欧无法在有限的操作下使得x不小于y,则输出-1。
否则输出一个整数,代表小欧的操作次数。

示例1
输入
3 40
4
2 3 4 4
输出
3
说明
第一次操作,选3,x变9,此时数组为[2,4,4]
第二次操作,选4,x变36,此时数组为[2]
第三次操作,选2,x变72,此时数组为空。三次操作后x不小于y。
操作的方式不是唯一的,但可以证明操作的最小次数为3.

示例2
输入
2 5
5
2 2 2 2 2
输出
-1
说明
选2后,x变4,此时数组为空。无法继续操作,x永远不可能不小于y。

示例3
输入
5 5
5
2 2 2 2 2
输出
0
说明
小欧不需要任何操作

关键算法说明:

​​去重处理​​:
    操作会删除数组中所有相同的值,因此每个数值只能使用一次
    使用set自动去重,确保每个数值只保留一次

​​忽略值1​​:
    乘以1不会改变当前乘积
    操作1会浪费操作次数,因此完全忽略

​​贪心策略​​:
    将操作数按降序排序(从大到小)
    优先使用较大的数值(可以使乘积更快增长)
    在最少操作次数内达到目标值

​​及时终止​​:
    只要当前乘积达到目标值就立即停止
    输出当前操作次数(最少操作数)

#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;

int main() {
    // 关闭同步流以提高I/O性能
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    long long x, y;
    int n;
    cin >> x >> y;      // 读取初始值x和目标值y
    cin >> n;           // 读取数组大小
    
    // 如果x已经大于等于y,不需要操作
    if (x >= y) {
        cout << 0 << endl;
        return 0;
    }
    
    vector<long long> a;
    set<long long> unique_vals; // 用于去重
    
    // 读取数组元素并去重
    for (int i = 0; i < n; i++) {
        long long val;
        cin >> val;
        // 只保留大于1的值(因为1不影响结果)
        if (val > 1) {
            // 使用set自动去重
            if (unique_vals.find(val) == unique_vals.end()) {
                unique_vals.insert(val);
                a.push_back(val);
            }
        }
    }
    
    // 如果没有有效的操作数
    if (a.empty()) {
        cout << -1 << endl;
        return 0;
    }
    
    // 将操作数按降序排序(优先选择大数)
    sort(a.begin(), a.end(), greater<long long>());
    
    long long cur = x;  // 当前乘积
    int cnt = 0;        // 操作次数
    
    // 遍历所有有效的操作数
    for (int i = 0; i < a.size(); i++) {
        // 执行乘法操作
        cur *= a[i];
        cnt++;
        
        // 检查是否达到目标
        if (cur >= y) {
            cout << cnt << endl;
            return 0;
        }
    }
    
    // 所有操作数用完仍未达到目标
    cout << -1 << endl;
    return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到