豆包MarsCode AI 刷题

43 阅读4分钟

今天我们将在豆包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> 来记录每个元素出现的次数。

算法步骤

  1. 统计元素出现次数
    • 遍历所有集合,将每个元素及其出现次数记录在 map<int, int> 中。
  2. 计算期望值
    • 对于每个元素,计算它在两个集合中同时出现的概率。
    • 具体来说,如果一个元素在 notnull 个集合中出现,在 null 个集合中不出现,那么它出现在两个集合的并集中的概率是:
      • 在两个不同集合中出现的概率:(notnull * null) / (n * (n - 1) / 2)
      • 在同一个集合中出现的概率:(notnull * (notnull - 1) / 2) / (n * (n - 1) / 2)
    • 将所有元素的概率加起来,得到期望值。
  3. 格式化输出
    • 使用 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 来记录每个元素的当前位置。

算法步骤

  1. 初始化位置数组
    • 使用 allPos 数组记录每个元素在 a 中的当前位置。allPos[i] 表示元素 i 在 a 中的位置。
  2. 遍历数组
    • 由左到右遍历数组 a,检查每个元素是否在其正确的位置上(即 a[i] 是否等于 i+1)。
  3. 检查和交换
    • 如果 a[i] 不在正确的位置上,查找 i+1 的位置 pos。
    • 检查 pos 和 i 的奇偶性是否相同。如果不同,则无法通过题目要求的交换方式使数组有序,返回 -1。
    • 如果奇偶性相同,则进行交换,并更新 allPos 数组中的位置信息,同时增加交换次数 ret。
  4. 返回结果
    • 遍历结束后,返回交换次数 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; }