这里是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 1≤n≤100。
- 每道菜的价格 a i a_i ai 满足 1 ≤ a i ≤ 1000 1 \leq a_i \leq 1000 1≤ai≤1000。
- 满减价格 x x x 和红包价格 y y y 满足 1 ≤ x , y ≤ 1000 1 \leq x, y \leq 1000 1≤x,y≤1000。
【题目解析】
问题分析
可以通过计算每道菜的价格总和,然后根据满减条件和红包金额来计算最终支付金额。我们计算出所有菜品的总价格。然后,如果总价格超过了满减条件,我们可以从总价格中减去满减的金额。最后,我们再减去红包的金额,即可得到最终支付金额。
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 小姐在整理文档时,发现了一些单词的大小写使用不规范。她希望通过最少的编辑次数,将这些单词转换成合法的格式。
合法的单词格式有三种:
- 全小写(如 good)
- 全大写(如 APP)
- 或首字母大写后续小写(如 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 1≤n,q≤105
1 ≤ x i ≤ n 1 \leq x_i \leq n 1≤xi≤n
1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1≤ai≤109
输出格式
输出一个整数,代表操作结束后所有元素之和对 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 1≤n,q≤105, 1 ≤ x i ≤ n 1 \leq x_i \leq n 1≤xi≤n, 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1≤ai≤109。
【题目解析】
解题思路
这道题需要考虑每次操作后数组元素的变化。每次操作都会将选定元素保持不变,而其他所有元素翻倍。因此,我们可以考虑计算每个元素被翻倍的次数,然后根据这个次数来计算最终的结果。
首先,我们初始化一个数组 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 1≤n≤2×105
1 ≤ a i ≤ 2 1 \leq a_i \leq 2 1≤ai≤2
输出描述
输出一个正整数,代表所有区间的众数之和。
样例输入
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 1≤n≤2×105, 1 ≤ a i ≤ 2 1 \leq a_i \leq 2 1≤ai≤2。
【题目解析】
这个问题可以通过遍历所有可能的区间,并统计每个区间中出现次数最多的数来解决。然后将每个区间中出现次数最多的数累加起来,得到最终的结果。
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 1≤n≤2×105
1 ≤ a i ≤ n 1 \leq a_i \leq n 1≤ai≤n
输出描述
输出 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 1≤n≤2×105, 1 ≤ a i ≤ n 1 \leq a_i \leq n 1≤ai≤n。
【题目解析】
这个问题需要分析每个位置 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;
}