今天我们将在豆包MarsCode AI刷题平台上,完成《小R的并集大小期望计算》与《小R的排列挑战》这两个算法问题,通过这些练习提升用户解决此类问题的能力
《小R的并集大小期望计算》题面如下:
问题描述
小R有 n 个集合,她想通过随机选择两个集合,并计算它们的并集,来求出这个并集大小的期望值。每个集合中的元素都是唯一的且互不相同。她需要计算出随机选择两个集合并集大小的期望值,并且要求结果保留两位小数。
保证输入至少有两个集合。
测试样例
样例1:
输入:n = 2,st = [[1, 2], [1, 3, 5]]
输出:'4.00'
样例2:
输入:n = 3,st = [[1, 4], [2, 5], [3, 6, 7]]
输出:'4.67'
样例3:
输入:n = 2,st = [[10, 20, 30], [10, 30, 50, 70]]
输出:'5.00'
问题理解
要求计算随机选择两个集合的并集大小的期望值。每个集合中的元素都是唯一的且互不相同。
数据结构选择
通过 map<int, int> 来记录每个元素出现的次数。
算法步骤
- 统计元素出现次数:
-
- 遍历所有集合,将每个元素及其出现次数记录在 map<int, int> 中。
- 计算期望值:
-
- 对于每个元素,计算它在两个集合中同时出现的概率。
- 具体来说,如果一个元素在 notnull 个集合中出现,在 null 个集合中不出现,那么它出现在两个集合的并集中的概率是:
-
- 在两个不同集合中出现的概率:(notnull * null) / (n * (n - 1) / 2)
- 在同一个集合中出现的概率:(notnull * (notnull - 1) / 2) / (n * (n - 1) / 2)
- 将所有元素的概率加起来,得到期望值。
- 格式化输出:
-
- 使用 ostringstream 将期望值格式化为保留两位小数的字符串。
具体实现 #include <bits/stdc++.h>
using namespace std;
string solution(int n, vector<vector> st) {
// write code here
// 统计每个元素出现的次数
map<int,int> allNums;
for(auto& x : st){
for(int y : x){
allNums[y]++;
}
}
// 计算期望值
double ret=0;
for(auto& x : allNums){
int notnull = x.second, null = n - notnull;
ret += (notnull * null + notnull * (notnull - 1) / 2.0) / (n * (n - 1) / 2.0);
}
// 保留两位小数并转换为字符串
ostringstream oss;
oss << fixed << setprecision(2) << ret;
return oss.str();
}
int main() {
cout << fixed << setprecision(2) << (solution(2, {{1, 2}, {1, 3, 5}}) == "4.00") << endl;
cout << fixed << setprecision(2) << (solution(3, {{1, 4}, {2, 5}, {3, 6, 7}}) == "4.67") << endl;
cout << fixed << setprecision(2) << (solution(2, {{10, 20, 30}, {10, 30, 50, 70}}) == "5.00") << endl;
return 0;
}
- 时间复杂度:O(n * m + k)
- 空间复杂度:O(k)
其中 n 是集合的数量,m 是每个集合的平均大小,k 是所有集合中不同元素的总数。
**《小R的排列挑战》题面如下:**
问题描述
小R有一个长度为 n 的排列,排列中的数字是 1 到 n 的整数。她每次操作可以选择两个数 a_i 和 a_j 进行交换,前提是这两个数的下标 i 和 j 的奇偶性相同(即同为奇数或同为偶数)。小R希望通过最少的操作使数组变成升序排列。
请你帮小R计算,最少需要多少次操作才能使得数组有序。如果不能通过这样的操作使数组有序,则输出 -1。
测试样例
样例1:
输入:n = 5, a = [1, 4, 5, 2, 3]
输出:2
样例2:
输入:n = 4, a = [4, 3, 2, 1]
输出:-1
样例3:
输入:n = 6, a = [2, 4, 6, 1, 3, 5]
输出:-1
问题理解
题目要求对一个长度为 n 的排列进行排序,但只能交换位置下标奇偶性相同的元素。目标是计算出最少的交换次数,使数组变成升序排列。如果无法通过这种交换方式使数组有序,则输出 -1。
数据结构选择
在原数组 a 上操作,并使用另一个数组 allpos 来记录每个元素的当前位置。
算法步骤
- 初始化位置数组:
-
- 使用 allPos 数组记录每个元素在 a 中的当前位置。allPos[i] 表示元素 i 在 a 中的位置。
- 遍历数组:
-
- 由左到右遍历数组 a,检查每个元素是否在其正确的位置上(即 a[i] 是否等于 i+1)。
- 检查和交换:
-
- 如果 a[i] 不在正确的位置上,查找 i+1 的位置 pos。
- 检查 pos 和 i 的奇偶性是否相同。如果不同,则无法通过题目要求的交换方式使数组有序,返回 -1。
- 如果奇偶性相同,则进行交换,并更新 allPos 数组中的位置信息,同时增加交换次数 ret。
- 返回结果:
-
- 遍历结束后,返回交换次数 ret。
具体实现
#include #include #include using namespace std; int solution(int n, vector& a) { int ret = 0; vector allPos(n + 1, -1); // 初始化位置数组 for (int i = 0; i < n; i++) { allPos[a[i]] = i; } // 遍历数组 for (int i = 0; i < n; i++) { if (a[i] != i + 1) { int pos = allPos[i + 1]; // 检查奇偶性 if (pos % 2 != i % 2) { return -1; } else { // 交换元素 ret++; allPos[a[i]] = pos; allPos[i + 1] = i; swap(a[i], a[pos]); } } } return ret; } int main() { vector a1 = {1, 4, 5, 2, 3}; cout << (solution(5, a1) == 2) << endl; vector a2 = {4, 3, 2, 1}; cout << (solution(4, a2) == -1) << endl; vector a3 = {2, 4, 6, 1, 3, 5}; cout << (solution(6, a3) == -1) << endl; }