青训营X豆包MarsCode刷题 119 游戏队友搜索 | 豆包MarsCode AI 刷题

113 阅读4分钟

问题描述

在一款多人游戏中,每局比赛需要多个玩家参与。如果发现两名玩家至少一起玩过两局比赛,则可以认为这两名玩家互为队友。现在你有一份玩家(通过玩家ID标识)和比赛局次(通过比赛ID标识)的历史记录表,目标是帮助某位指定玩家找到所有符合条件的队友。

例如,已知以下比赛历史记录:

| 玩家ID | 游戏ID |

测试样例

样例1:

输入:id = 1, num = 10, array = [[1,1], [1,2], [1,3], [2,1], [2,4], [3,2], [4,1], [4,2], [5,2], [5,3]]
输出:[4, 5]

样例2:

输入:id = 2, num = 6, array = [[2,1], [2,3], [1,1], [1,2], [3,1], [4,3]]
输出:[]

样例3:

输入:id = 3, num = 8, array = [[3,1], [3,2], [3,3], [4,1], [5,2], [6,3], [7,1], [7,2]]
输出:[7]

题目思路

  1. 分析关系

    • 游戏和玩家构成了一个双向关系:玩家参与多个游戏,多个玩家也可以参与同一个游戏。
    • 需要找出与目标玩家有较高“共同参与度”的玩家,通过统计共同游戏的次数来筛选。
  2. 算法设计

    • 构建“游戏-玩家”映射,记录每个游戏对应的所有玩家。
    • 找出目标玩家参与过的所有游戏。
    • 对这些游戏的玩家进行统计,记录每个玩家与目标玩家的共同参与次数。
    • 返回共同参与次数不少于2次的玩家列表,并按升序排序。

代码实现

以下C++实现:

#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> findTeammates(int id, int num, const vector<vector<int>>& array) {
    // 游戏ID -> 玩家集合
    unordered_map<int, unordered_set<int>> game_to_players;
    
    // 构建游戏与玩家的映射
    for (const auto& record : array) {
        int player = record[0];
        int game = record[1];
        game_to_players[game].insert(player);
    }
    
    // 目标玩家参与的游戏ID集合
    unordered_set<int> target_games;
    for (const auto& record : array) {
        if (record[0] == id) {
            target_games.insert(record[1]);
        }
    }
    
    // 玩家出现次数统计(与目标玩家共同参加的次数)
    unordered_map<int, int> teammate_count;
    for (int game : target_games) {
        for (int player : game_to_players[game]) {
            if (player != id) {
                teammate_count[player]++;
            }
        }
    }
    
    // 找出共同游戏次数 >= 2 的玩家
    vector<int> teammates;
    for (const auto& [player, count] : teammate_count) {
        if (count >= 2) {
            teammates.push_back(player);
        }
    }
    
    // 按升序排序输出结果
    sort(teammates.begin(), teammates.end());
    return teammates;
}

int main() {
    // Add your test cases here
    
    std::vector<int> result = solution(1, 10, {{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 4}, {3, 2}, {4, 1}, {4, 2}, {5, 2}, {5, 3}});
    std::cout << (result == std::vector<int>{4, 5}) << std::endl;

    return 0;
}

二、知识总结:基于映射与统计的解决思路

新知识点

  1. 双重映射关系的构建
    使用 unordered_map 实现游戏到玩家集合的映射,解决了“如何快速获取所有参与某游戏的玩家”问题。

    unordered_map<int, unordered_set<int>> game_to_players;
    
  2. 统计与筛选结合
    通过 unordered_map 对玩家的共同参与次数进行计数,实现了筛选逻辑:

    unordered_map<int, int> teammate_count;
    

    只需要一次遍历即可记录次数,复杂度为 O(n)O(n)。

  3. 升序排序结果
    输出结果需要按升序排列,使用 std::sort 进行排序:

    sort(teammates.begin(), teammates.end());
    

个人理解与建议

  • 这道题突出了对数据结构的理解与灵活使用。初学者应熟练掌握常用容器(如 unordered_mapunordered_set)及其基本操作(插入、查找、遍历)。
  • 遇到类似的“关系类”问题时,可以优先考虑图或双向映射关系建模,以快速找到算法的切入点。

三、学习计划:利用刷题提升逻辑思维

高效刷题的策略

  1. 明确目标
    每日完成1-2道思考性强的题目,尤其是涉及数据结构与算法结合的问题,培养建模能力。
  2. 逐步深化
    从简单题(如哈希表基本操作)入手,逐步拓展到复杂题型(如本题中的映射关系和多条件筛选)。
  3. 错题记录
    对于未通过的测试案例,重点分析原因(如边界条件、遗漏情况),并将优化后的代码与原始实现进行对比。

四、工具运用:结合AI与学习资源

结合MarsCode AI

  • 使用MarsCode的题目推荐功能,根据自身水平选择适合的题目类型(如哈希表、图等)。
  • 利用其代码分析与优化功能,发现代码中的潜在问题或可优化之处。

其他学习资源

  1. LeetCode:丰富的题目库是刷题的有力补充。
  2. 算法书籍:《程序员面试金典》《算法导论》适合作为理论补充,提升算法设计能力。
  3. 实践项目:将刷题中掌握的知识应用到实际项目中,例如实现一个推荐系统或游戏匹配功能。

五、总结

通过本次题目的解析与代码实现,我们深入理解了如何通过映射与统计结合解决复杂的筛选问题。这种思路不仅在算法刷题中常见,在实际工程应用中同样重要。希望本文能对正在学习算法的同学提供帮助,也期待与更多人交流学习心得,共同进步!