青训营X豆包MarsCode 代码详解 day4 | 豆包MarsCode AI 刷题

81 阅读7分钟

问题描述

小C参与了一场抢红包的游戏,现在他想要对所有参与抢红包的人进行一次运气排名。排名规则如下:

  1. 抢到的金额越多,排名越靠前。
  2. 如果两个人抢到的金额相同,则按照他们抢红包的顺序进行排名。

测试样例

样例1:

输入:n = 4 ,s = ["a", "b", "c", "d"] ,x = [1, 2, 2, 1] 输出:['b', 'c', 'a', 'd']

样例2:

输入:n = 3 ,s = ["x", "y", "z"] ,x = [100, 200, 200] 输出:['y', 'z', 'x']

样例3:

输入:n = 5 ,s = ["m", "n", "o", "p", "q"] ,x = [50, 50, 30, 30, 20] 输出:['m', 'n', 'o', 'p', 'q']

这道题的目标是对参与抢红包的人员进行排名,排名规则是根据抢到的总金额进行降序排列,如果金额相同,则按照他们抢红包的顺序进行排名。以下是详细的题解步骤:

题解步骤:

1. 数据结构选择:

  • 使用LinkedHashMap来存储每个参与者的名字和他们抢到的总金额。

  • LinkedHashMap的选择是因为它可以保持插入顺序,这样在金额相同的情况下,参与者的顺序不会被打乱。

2. 累加金额:

  • 遍历输入的名字数组s和金额数组x。

  • 对于每个参与者,将其抢到的金额累加到Map中。

  • 使用getOrDefault方法来处理参与者可能多次出现的情况。

3. 排序:

  • 将Map的条目转换为一个List,以便进行排序。

  • 使用Collections.sort对列表进行排序。

  • 自定义比较器Comparator,首先根据金额进行降序排序。

4. 提取结果:

  • 从排序后的列表中提取参与者的名字,形成最终的排名结果。

5. 时间复杂度:

  • 主要的时间消耗在排序步骤,时间复杂度为O(n log n),其中n是参与者的数量。

代码实现:


public class Main {
    public static List<String> solution(int n, List<String> s, List<Integer> x) {
        // 使用Map来累加每个参与者的总金额
        Map<String, Integer> participantMap = new LinkedHashMap<>();
        
        for (int i = 0; i < n; i++) {
            participantMap.put(s.get(i), participantMap.getOrDefault(s.get(i), 0) + x.get(i));
        }
        
        // 将Map转换为List以便排序
        List<Map.Entry<String, Integer>> participantList = new ArrayList<>(participantMap.entrySet());
        
        // 对列表进行排序
        Collections.sort(participantList, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> e1, Map.Entry<String, Integer> e2) {
                // 按金额降序排序
                return e2.getValue().compareTo(e1.getValue());
            }
        });
        
        // 提取排序后的名字
        List<String> result = new ArrayList<>();
        for (int i = 0; i < participantList.size(); i++) {
            result.add(i, participantList.get(i).getKey());
        }
        
        return result;
    }

    public static void main(String[] args) {
        System.out.println(solution(4, Arrays.asList("a", "b", "c", "d"), Arrays.asList(1, 2, 2, 1)).equals(Arrays.asList("b", "c", "a", "d")));
        System.out.println(solution(3, Arrays.asList("x", "y", "z"), Arrays.asList(100, 200, 200)).equals(Arrays.asList("y", "z", "x")));
        System.out.println(solution(5, Arrays.asList("m", "n", "o", "p", "q"), Arrays.asList(50, 50, 30, 30, 20)).equals(Arrays.asList("m", "n", "o", "p", "q")));
    }
}

问题描述

小R正在组织一个比赛,比赛中有 n 支队伍参赛。比赛遵循以下独特的赛制:

  • 如果当前队伍数为 偶数,那么每支队伍都会与另一支队伍配对。总共进行 n / 2 场比赛,且产生 n / 2 支队伍进入下一轮。
  • 如果当前队伍数为 奇数,那么将会随机轮空并晋级一支队伍,其余的队伍配对。总共进行 (n - 1) / 2 场比赛,且产生 (n - 1) / 2 + 1 支队伍进入下一轮。

小R想知道在比赛中进行的配对次数,直到决出唯一的获胜队伍为止。


测试样例

样例1:

输入:n = 7
输出:6

样例2:

输入:n = 14
输出:13

样例3:

输入:n = 1
输出:0

解题思路

这个问题可以通过模拟比赛的进行来解决。我们需要计算在比赛中进行的配对次数,直到决出唯一的获胜队伍为止。

理解问题

  1. 初始队伍数 n:我们需要从 n 支队伍开始。

  2. 比赛规则

    • 如果队伍数为偶数,每支队伍都会与另一支队伍配对,进行 n / 2 场比赛,产生 n / 2 支队伍进入下一轮。
    • 如果队伍数为奇数,随机轮空并晋级一支队伍,其余的队伍配对,进行 (n - 1) / 2 场比赛,产生 (n - 1) / 2 + 1 支队伍进入下一轮。
  3. 目标:计算从 n 支队伍到决出唯一获胜队伍为止的总配对次数。

数据结构与算法

  • 数据结构:我们只需要一个整数来记录当前的队伍数 n 和另一个整数来记录配对次数 count

  • 算法步骤

    1. 初始化 count 为 0。

    2. 当 n 大于 1 时,循环执行以下步骤:

      • 如果 n 是偶数,进行 n / 2 场比赛,n 变为 n / 2count 增加 n / 2
      • 如果 n 是奇数,进行 (n - 1) / 2 场比赛,n 变为 (n - 1) / 2 + 1count 增加 (n - 1) / 2
    3. 返回 count

代码

    public static int solution(int n) {
        // write code here
        int count = 0;
        while (n > 1) {
            if (n % 2 == 0) {
                n /= 2;
                count += n;
            } else {
                n = (n - 1) / 2;
                count += n;
                n++;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        System.out.println(solution(7) == 6);
        System.out.println(solution(14) == 13);
        System.out.println(solution(1) == 0);
    }
}

卡牌翻面求和问题

问题描述

小M有 nn 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 aiai​,背面是 bibi​。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 109+7109+7 取模。

例如:如果有3张卡牌,正反面数字分别为 (1,2)(2,3) 和 (3,2),你需要找到所有满足这3张卡牌正面或背面朝上的数字之和可以被3整除的组合数。


测试样例

样例1:

输入:n = 3 ,a = [1, 2, 3] ,b = [2, 3, 2]
输出:3

样例2:

输入:n = 4 ,a = [3, 1, 2, 4] ,b = [1, 2, 3, 1]
输出:6

样例3:

输入:n = 5 ,a = [1, 2, 3, 4, 5] ,b = [1, 2, 3, 4, 5]
输出:32

题目描述

小M有 (n) 张卡牌,每张卡牌的正反面分别写着不同的数字,正面是 (a_i),背面是 (b_i)。小M希望通过选择每张卡牌的一面,使得所有向上的数字之和可以被3整除。你需要告诉小M,一共有多少种不同的方案可以满足这个条件。由于可能的方案数量过大,结果需要对 (10^9+7) 取模。

解题思路

  1. 枚举所有组合

    • 每张卡牌有两种选择(正面或背面),因此总共有 (2^n) 种可能的组合。
    • 我们可以使用位运算来枚举所有组合。
  2. 检查每种组合的和

    • 对于每一种组合,计算所有卡牌正面或背面朝上的数字之和。
    • 检查这个和是否能被3整除。
  3. 统计满足条件的组合数

    • 统计所有满足条件的组合数,并对结果取模 (10^9 + 7)。

代码实现

    static final int MOD = 1000000007;
    public static int solution(int n, int[] a, int[] b) {
        int count = 0;
        // 总共有2^n种可能的组合
        int totalCombinations = 1 << n;
        
        // 遍历所有可能的组合
        for (int mask = 0; mask < totalCombinations; mask++) {
            int sum = 0;
            
            // 检查每一位,0表示选择正面,1表示选择背面
            for (int i = 0; i < n; i++) {
                if ((mask & (1 << i)) == 0) {
                    // 第i位是0,选择正面
                    sum += a[i];
                } else {
                    // 第i位是1,选择背面
                    sum += b[i];
                }
            }
            
            // 检查sum是否能被3整除
            if (sum % 3 == 0) {
                count = (count + 1) % MOD;
            }
        }
        
        return count;
    }

    public static void main(String[] args) {
        System.out.println(solution(3, new int[]{1, 2, 3}, new int[]{2, 3, 2}) == 3);
        System.out.println(solution(4, new int[]{3, 1, 2, 4}, new int[]{1, 2, 3, 1}) == 6);
        System.out.println(solution(5, new int[]{1, 2, 3, 4, 5}, new int[]{1, 2, 3, 4, 5}) == 32);
    }
}

代码解释

  1. 枚举所有组合

    • for (int mask = 0; mask < (1 << n); mask++):使用位运算 mask 来表示每张卡牌的选择(正面或背面)。1 << n 表示 (2^n) 种可能的组合。
  2. 计算当前组合的和

    • for (int i = 0; i < n; i++):遍历每张卡牌。

    • if ((mask & (1 << i)) != 0):检查 mask 的第 i 位是否为 1

      • 如果为 1,选择正面 a[i]
      • 如果为 0,选择背面 b[i]
  3. 检查和是否能被3整除

    • if (sum % 3 == 0):检查当前组合的和是否能被3整除。
    • 如果满足条件,统计该组合数,并对结果取模 (10^9 + 7)。
  4. 返回结果

    • return count;:返回满足条件的组合数。

复杂度分析

  • 时间复杂度:(O(2^n \times n)),其中 (n) 是卡牌的数量。
  • 空间复杂度:(O(1)),只需要常数空间来存储中间变量。