问题描述
在一款多人游戏中,每局比赛需要多个玩家参与。如果发现两名玩家至少一起玩过两局比赛,则可以认为这两名玩家互为队友。现在你有一份玩家(通过玩家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]
详细解析与思路
这道题目涉及 数据处理与映射关系的管理,需要通过分析游戏历史记录,找到符合条件的队友。以下是逐步解析思路和关键步骤:
问题分析
-
题目核心:
- 给定玩家
id
和历史记录表,找到至少和该玩家同场玩过 两局及以上 的其他玩家。
- 给定玩家
-
解法关键:
- 每场游戏玩家分组:通过比赛 ID 找到每局比赛的玩家。
- 统计队友频率:统计与指定玩家一起玩过的玩家及其共同参与的比赛次数。
- 筛选有效队友:只保留共同比赛次数 >= 2 的玩家。
知识总结与思路整理
-
数据结构的选择:
std::unordered_map
:用于记录每个游戏 ID 对应的玩家集合。它提供 O(1) 的插入和查找效率,非常适合这种需要快速定位数据的场景。std::unordered_set
:存储每场比赛的玩家,避免重复,方便统计。
-
算法步骤分解:
- 遍历输入的比赛记录,将每场比赛的玩家归类到对应的比赛 ID。
- 检查目标玩家是否参与某场比赛,记录与目标玩家共同参赛的玩家及其次数。
- 筛选符合条件的玩家(出现次数 >= 2),并按升序排序。
代码详解
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
// 主函数:根据指定玩家ID,找到符合条件的队友
std::vector<int> solution(int id, int num, std::vector<std::vector<int>> array) {
// 1. 将每场比赛的玩家集合记录到 gameToPlayers 映射中
std::unordered_map<int, std::unordered_set<int>> gameToPlayers;
for (const auto& entry : array) {
int playerID = entry[0]; // 玩家ID
int gameID = entry[1]; // 游戏ID
gameToPlayers[gameID].insert(playerID); // 玩家加入对应的游戏集合
}
// 2. 统计与目标玩家一起玩的玩家次数
std::unordered_map<int, int> teammateCount;
for (const auto& [gameID, players] : gameToPlayers) {
if (players.count(id)) { // 如果目标玩家参与了这局游戏
for (int player : players) { // 遍历同场玩家
if (player != id) { // 排除目标玩家自身
teammateCount[player]++; // 计数 +1
}
}
}
}
// 3. 找到符合条件的队友(出现次数 >= 2),并保存到结果
std::vector<int> result;
for (const auto& [player, count] : teammateCount) {
if (count >= 2) { // 队友条件判断
result.push_back(player);
}
}
// 4. 按升序排序队友ID
std::sort(result.begin(), result.end());
return result;
}
int main() {
// 测试样例1
std::vector<int> result1 = solution(1, 10, {{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 4}, {3, 2}, {4, 1}, {4, 2}, {5, 2}, {5, 3}});
for (int player : result1) {
std::cout << player << " ";
}
std::cout << std::endl;
// 测试样例2
std::vector<int> result2 = solution(2, 6, {{2, 1}, {2, 3}, {1, 1}, {1, 2}, {3, 1}, {4, 3}});
for (int player : result2) {
std::cout << player << " ";
}
std::cout << std::endl;
// 测试样例3
std::vector<int> result3 = solution(3, 8, {{3, 1}, {3, 2}, {3, 3}, {4, 1}, {5, 2}, {6, 3}, {7, 1}, {7, 2}});
for (int player : result3) {
std::cout << player << " ";
}
std::cout << std::endl;
return 0;
}
代码运行过程解析
以样例1为例:
输入:
玩家ID = 1
,比赛记录表:
[[1, 1], [1, 2], [1, 3], [2, 1], [2, 4], [3, 2], [4, 1], [4, 2], [5, 2], [5, 3]]
Step 1:构建游戏玩家映射
创建 gameToPlayers
:
1: {1, 2, 4}
2: {1, 3, 4, 5}
3: {1, 5}
4: {2}
Step 2:统计与目标玩家的共同比赛次数
目标玩家 1
参与了游戏 1, 2, 3
:
- 游戏
1
:玩家{2, 4}
,计数为{2: 1, 4: 1}
。 - 游戏
2
:玩家{3, 4, 5}
,计数为{2: 1, 4: 2, 3: 1, 5: 1}
。 - 游戏
3
:玩家{5}
,计数为{2: 1, 4: 2, 3: 1, 5: 2}
。
Step 3:筛选符合条件的队友
队友条件:计数 ≥ 2,结果为 {4, 5}
。
Step 4:排序结果
最终输出:[4, 5]
。
对入门同学的学习建议
-
理解题意与数据处理逻辑:
- 分析题目需求,明确核心目标是统计玩家间的共同参赛次数。
- 划分问题步骤,通过多层数据结构(
unordered_map
和unordered_set
)组织数据,降低复杂度。
-
掌握 STL 容器的应用:
- 学会使用
unordered_map
和unordered_set
来高效存储和查找数据。 - 使用
std::sort
对结果排序,注意算法的时间复杂度。
- 学会使用
-
分解与调试代码:
- 将问题分解成小任务,例如先构建映射、统计次数、再筛选和排序。
- 利用调试工具逐步验证数据流是否正确,特别是多层嵌套的循环和逻辑判断。
-
代码优化与可读性:
- 为关键变量和数据结构命名,增加注释,方便自己和他人阅读。
- 尝试优化逻辑,减少不必要的计算。