【笔试题汇总】美团春招笔试 第二场 2024.03.16

这里是paoxiaomo,一个现役ACMer,之后将会持续更新算法笔记系列以及笔试题题解系列
本文章面向想打ICPC/蓝桥杯/天梯赛等程序设计竞赛,以及各个大厂笔试的选手
感谢大家的订阅➕ 和 喜欢💗
有什么想看的算法专题可以私信博主

(本文题面由清隆学长收集)

01.外卖会员日

题目背景

美食会员日到来,K小姐决定享受这个给自己带来额外优惠的日子。她在美团外卖上挑选了几样心仪的美食,期待在享受美味的同时也能享受优惠。面对屏幕上的满减与红包叠加计算,K小姐希望能快速得知最终的支付金额。

问题描述

K小姐在会员日时点了若干道菜,已知每道菜的价格、满减条件以及红包金额,请求出她最终需要支付的金额。

输入格式

第一行包含一个正整数 n n n,代表菜品总数。

第二行包含 n n n 个正整数 a i a_i ai,代表每道菜的价格。

第三行包含两个正整数 x x x y y y x x x 代表满减的价格, y y y 代表红包的价格。

输出格式

输出一个正整数,代表 K小姐最终应付的钱数。

样例输入

4
10 20 10 20
25 10

样例输出

25

评测数据与规模

  • 对于 100 % 100\% 100% 的评测数据,满足 1 ≤ n ≤ 100 1 \leq n \leq 100 1n100
  • 每道菜的价格 a i a_i ai 满足 1 ≤ a i ≤ 1000 1 \leq a_i \leq 1000 1ai1000
  • 满减价格 x x x 和红包价格 y y y 满足 1 ≤ x , y ≤ 1000 1 \leq x, y \leq 1000 1x,y1000

【题目解析】

问题分析

可以通过计算每道菜的价格总和,然后根据满减条件和红包金额来计算最终支付金额。我们计算出所有菜品的总价格。然后,如果总价格超过了满减条件,我们可以从总价格中减去满减的金额。最后,我们再减去红包的金额,即可得到最终支付金额。

cpp

#include <iostream>
#include <vector>

using namespace std;

int main() {
    int n; // 菜品总数
    cin >> n;

    // 存储每道菜的价格
    vector<int> prices(n);
    for (int i = 0; i < n; ++i) {
        cin >> prices[i];
    }

    int x, y; // 满减条件和红包金额
    cin >> x >> y;

    // 计算总价格
    int total_price = 0;
    for (int i = 0; i < n; ++i) {
        total_price += prices[i];
    }

    // 如果总价格超过满减条件,则减去满减金额
    if (total_price >= x) {
        total_price -= y;
    }

    // 输出最终支付金额
    cout << total_price << endl;

    return 0;
}

02.K 小姐的单词规范化挑战

题目背景

K 小姐在整理文档时,发现了一些单词的大小写使用不规范。她希望通过最少的编辑次数,将这些单词转换成合法的格式。

合法的单词格式有三种:

  1. 全小写(如 good)
  2. 全大写(如 APP)
  3. 或首字母大写后续小写(如 Alice)

现在,K 小姐需要你的帮助来完成这项挑战。

问题描述

给定一个字符串,该字符串只包含大写和小写字母。请计算最少需要多少次操作,才能将这个字符串修改为一个合法的单词。每次操作可以更改任意一个字符的大小写。

输入格式

输入只有一行,包含一个仅由大写字母和小写字母组成的字符串,长度不超过 1 0 5 10^5 105

输出格式

输出一个整数,代表将字符串修改为合法单词的最小操作次数。

样例输入

AbC

样例输出

1

评测数据与规模

对于 100 % 100\% 100% 的数据,字符串长度不超过 1 0 5 10^5 105

【题目解析】

这个问题可以通过遍历字符串并检查每个字符的大小写来解决。然后根据规则,对不合法的字符进行大小写转换,以使其符合合法单词的格式。

cpp

#include <iostream>
#include <algorithm>
#include <string>

using namespace std;

int minOperations(string s) {
    int lowerCount = 0, upperCount = 0;

    // 统计字符串中小写字母和大写字母的个数
    for(char c : s) {
        if(islower(c))
            lowerCount++;
        else
            upperCount++;
    }

    // 根据规则确定最少操作次数
    if(lowerCount >= upperCount) {
        // 全部转换为小写字母
        return upperCount;
    } else {
        // 全部转换为大写字母
        return lowerCount;
    }
}

int main() {
    string s;
    cin >> s;

    int result = minOperations(s);
    cout << result << endl;

    return 0;
}

03.K 小姐的数组操作

题目背景

K 小姐在研究数学时,发现了一个有趣的数组操作游戏。她会选定数组中的一个元素,然后将其他所有元素翻倍。现在,K 小姐想知道,在进行了一系列操作后,数组所有元素的总和是多少。

问题描述

给定一个初始数组和操作次数,每次操作指定一个元素保持不变,而其他所有元素翻倍。请计算经过这些操作后,数组所有元素之和的结果。由于结果可能很大,需要对 1 0 9 + 7 10^9+7 109+7 取模后输出。

输入格式

第一行输入两个正整数 n , q n, q n,q,代表数组的大小和操作次数。

第二行输入 n n n 个正整数 a i a_i ai,代表数组的元素。

接下来的 q q q 行,每行输入一个正整数 x i x_i xi,代表第 i i i 次操作未被翻倍的元素。

1 ≤ n , q ≤ 1 0 5 1 \leq n, q \leq 10^5 1n,q105

1 ≤ x i ≤ n 1 \leq x_i \leq n 1xin

1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1ai109

输出格式

输出一个整数,代表操作结束后所有元素之和对 1 0 9 + 7 10^9+7 109+7 取模的值。

样例输入

4 2
1 2 3 4
1
2

样例输出

34

评测数据与规模

对于 100 % 100\% 100% 的数据,满足 1 ≤ n , q ≤ 1 0 5 1 \leq n, q \leq 10^5 1n,q105 1 ≤ x i ≤ n 1 \leq x_i \leq n 1xin 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1ai109

【题目解析】

解题思路

这道题需要考虑每次操作后数组元素的变化。每次操作都会将选定元素保持不变,而其他所有元素翻倍。因此,我们可以考虑计算每个元素被翻倍的次数,然后根据这个次数来计算最终的结果。

首先,我们初始化一个数组 count 用来记录每个元素被翻倍的次数,初始值为 0。然后,对于每次操作,我们将对应的元素计数加 1,同时更新数组所有其他元素的计数,即将它们的计数加上当前操作的次数。

最后,我们遍历数组 a 中的每个元素,并根据该元素被翻倍的次数来计算最终结果,将每个元素乘以 2 c o u n t [ i ] 2^{count[i]} 2count[i] 累加起来,并对 1 0 9 + 7 10^9+7 109+7 取模。

cpp

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

const int MOD = 1000000007;

int main() {
    int n, q;
    cin >> n >> q;

    vector<int> a(n);
    vector<int> count(n, 0);

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

    // 执行每次操作
    for (int i = 0; i < q; ++i) {
        int x;
        cin >> x;
        count[x - 1]++;
    }

    long long sum = 0;

    // 计算最终结果
    for (int i = 0; i < n; ++i) {
        sum = (sum + (long long)a[i] * (1LL << count[i])) % MOD;
    }

    cout << sum << endl;

    return 0;
}

04.众数和:K 小姐的数组区间挑战

题目描述

K 小姐得到了一个数组,并对数组内的数学性质产生了兴趣。她现在需要你的帮助来计算给定数组中所有可能区间的众数之和。

众数定义为在一个区间中出现次数最多的数。如果有多个数频次相同,则选取最小的那个作为众数。

输入描述

第一行输入一个正整数 n n n,代表数组的大小。

第二行输入 n n n 个正整数 a i a_i ai,代表数组的元素。

1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105

1 ≤ a i ≤ 2 1 \leq a_i \leq 2 1ai2

输出描述

输出一个正整数,代表所有区间的众数之和。

样例输入

3
2 1 2

样例输出

9

样例解释

以下是数组的所有区间及其众数:

  • 区间 [2] 的众数为 2。
  • 区间 [2, 1] 的众数为 1。
  • 区间 [2, 1, 2] 的众数为 2。
  • 区间 [1] 的众数为 1。
  • 区间 [1, 2] 的众数为 1。
  • 区间 [2] 的众数为 2。

众数之和为 2 + 1 + 2 + 1 + 1 + 2 = 9 2 + 1 + 2 + 1 + 1 + 2 = 9 2+1+2+1+1+2=9

评测数据与规模

对于 100 % 100\% 100% 的数据,满足 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105 1 ≤ a i ≤ 2 1 \leq a_i \leq 2 1ai2

【题目解析】

这个问题可以通过遍历所有可能的区间,并统计每个区间中出现次数最多的数来解决。然后将每个区间中出现次数最多的数累加起来,得到最终的结果。

cpp

#include <iostream>
#include <vector>
#include <unordered_map>

using namespace std;

int main() {
    int n;
    cin >> n;

    vector<int> arr(n);
    for(int i = 0; i < n; i++) {
        cin >> arr[i];
    }

    long long totalSum = 0; // 用 long long 存储结果,防止溢出
    unordered_map<int, int> freqMap; // 用于统计每个数在当前区间中出现的次数

    // 遍历所有可能的区间
    for(int i = 0; i < n; i++) {
        freqMap.clear(); // 清空频次统计

        // 统计当前区间中每个数的频次
        for(int j = i; j < n; j++) {
            freqMap[arr[j]]++;
            
            // 找到当前区间中出现次数最多的数(众数)
            int maxFreq = 0, mode = -1;
            for(auto it = freqMap.begin(); it != freqMap.end(); it++) {
                if(it->second > maxFreq || (it->second == maxFreq && it->first < mode)) {
                    maxFreq = it->second;
                    mode = it->first;
                }
            }

            // 将当前区间中出现次数最多的数累加到总和中
            totalSum += mode;
        }
    }

    cout << totalSum << endl;

    return 0;
}

05.K 小姐的排列逆序对挑战

题目描述

K 小姐有一个长度为 n n n 的排列,她定义了一个函数 f ( i ) f(i) f(i):将排列中第 i i i 个元素取反(即乘以 − 1 -1 1),然后计算新数组的逆序对数量。现在,她想知道对于排列中每一个位置 i i i f ( i ) f(i) f(i) 的值是多少。

排列是指一个长度为 n n n 的数组,其中包含从 1 1 1 n n n 的每个数字恰好一次。

输入描述

第一行输入一个正整数 n n n,代表排列的大小。

第二行输入 n n n 个正整数 a i a_i ai,代表排列的元素。

1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105

1 ≤ a i ≤ n 1 \leq a_i \leq n 1ain

输出描述

输出 n n n 个整数,第 i i i 个整数表示 f ( i ) f(i) f(i) 的值。

样例输入

3
1 2 3

样例输出

0 1 2

样例说明

  • 第一个元素取反,数组变为 [ − 1 , 2 , 3 ] [-1, 2, 3] [1,2,3],逆序对数量为 0 0 0
  • 第二个元素取反,数组变为 [ 1 , − 2 , 3 ] [1, -2, 3] [1,2,3],逆序对数量为 1 1 1
  • 第三个元素取反,数组变为 [ 1 , 2 , − 3 ] [1, 2, -3] [1,2,3],逆序对数量为 2 2 2

评测数据与规模

对于 100 % 100\% 100% 的数据,满足 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105 1 ≤ a i ≤ n 1 \leq a_i \leq n 1ain

【题目解析】

这个问题需要分析每个位置 i i i 进行函数 f ( i ) f(i) f(i) 操作后的逆序对数量。

首先,我们可以通过遍历数组,对于每个位置 i i i,计算其右侧比它小的元素数量。这可以通过使用树状数组或线段树来实现,以提高效率。然后,对于每个位置 i i i,我们可以计算其进行 f ( i ) f(i) f(i) 操作后的逆序对数量。

cpp

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// 树状数组(Fenwick Tree)实现
class FenwickTree {
private:
    vector<int> tree;

public:
    FenwickTree(int n) {
        tree.resize(n + 1, 0);
    }

    // 更新数组中指定位置的值
    void update(int idx, int val) {
        while (idx < tree.size()) {
            tree[idx] += val;
            idx += (idx & -idx); // 更新下一个节点
        }
    }

    // 查询数组中前缀和
    int query(int idx) {
        int sum = 0;
        while (idx > 0) {
            sum += tree[idx];
            idx -= (idx & -idx); // 移动到上一个节点
        }
        return sum;
    }
};

int main() {
    int n;
    cin >> n;

    vector<int> permutation(n);
    for (int i = 0; i < n; i++) {
        cin >> permutation[i];
    }

    FenwickTree fenwickTree(n);

    // 计算每个位置右侧比它小的元素数量,并更新树状数组
    vector<int> smallerCount(n);
    for (int i = n - 1; i >= 0; i--) {
        smallerCount[i] = fenwickTree.query(permutation[i] - 1);
        fenwickTree.update(permutation[i], 1);
    }

    // 计算每个位置进行 f(i) 操作后的逆序对数量
    vector<int> fValue(n);
    for (int i = 0; i < n; i++) {
        fValue[i] = smallerCount[i] + (n - 1 - i) - smallerCount[i];
    }

    // 输出结果
    for (int i = 0; i < n; i++) {
        cout << fValue[i] << " ";
    }
    cout << endl;

    return 0;
}
  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值