【华为OD】石头剪刀布游戏
【华为OD】石头剪刀布游戏
题目描述
石头剪刀布游戏有3种出拳形状:石头、剪刀、布。分别用字母 A、B、C 表示。
游戏规则:
- 出拳形状之间的胜负规则如下:A > B;B > C;C > A;">"左边一个字母,表示相对优势形状,右边一个字母,表示相对劣势形状。
- 当本场次中有且仅有一种出拳形状优于其它出拳形状,则该形状的玩家是胜利者。否则认为是平局。
- 当发生平局,没有赢家。有多个胜利者时,同为赢家。
例如1: 三个玩家出拳分别是A、B、C,由于出现三方优势循环(即没有任何一方优于其它出拳者),判断为平局。
例如2: 三个玩家,出拳分别是A、B,出拳A的获胜。
例如3: 三个玩家,出拳全部是A,判为平局。
输入描述
在一场游戏中,每个玩家的信息为一行。玩家数量不超过1000。每个玩家信息有2个字段,用空格隔开:
- 玩家ID:一个仅由英文字母和数字组成的字符串
- 出拳形状:以英文大写字母表示,A、B、C形状。
例:
abc1 A
xyz B
解释:玩家abc1出拳为石头(A)。玩家xyz出拳为剪刀(B)
输出描述
输出为赢家的玩家ID列表(一个或多个),每个ID一行,按字符串升序排列。如果没有赢家,输出为"NULL"字符串。
示例
示例一
输入:
abc1 A
xyz B
输出:
abc1
说明: A比B有优势,abc1胜出
示例二
输入:
abc1 A
xyz A
输出:
NULL
说明: 没有优胜的出拳形状,平局
示例三
输入:
abc1 A
def A
alic A
xyz B
输出:
abc1
alic
def
说明: A为优胜方,有三个赢家。
解题思路
这道题的核心是理解石头剪刀布的胜负规则,并判断是否存在唯一的获胜形状。
关键点:
- A > B,B > C,C > A(循环克制关系)
- 只有当场上存在且仅存在一种形状能够克制其他所有形状时,该形状的玩家才获胜
- 如果出现循环克制(如A、B、C都存在)或所有玩家出拳相同,则平局
解法一:统计形状种类判断
思路讲解
这种方法通过统计场上出现的不同形状种类来判断胜负:
- 如果只有一种形状,平局
- 如果有两种形状,根据克制关系判断获胜方
- 如果有三种形状,形成循环克制,平局
Java实现
import java.util.*;public class RockPaperScissors {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);List<String> players = new ArrayList<>();Map<String, Character> playerMoves = new HashMap<>();Set<Character> moves = new HashSet<>();String line;while (scanner.hasNextLine() && !(line = scanner.nextLine()).isEmpty()) {String[] parts = line.split(" ");String playerId = parts[0];char move = parts[1].charAt(0);players.add(playerId);playerMoves.put(playerId, move);moves.add(move);}// 判断获胜情况if (moves.size() == 1 || moves.size() == 3) {// 平局情况:只有一种形状或三种形状都存在System.out.println("NULL");} else {// 两种形状,判断获胜方Character[] moveArray = moves.toArray(new Character[0]);char move1 = moveArray[0];char move2 = moveArray[1];char winningMove = getWinner(move1, move2);// 收集获胜玩家并排序List<String> winners = new ArrayList<>();for (String player : players) {if (playerMoves.get(player) == winningMove) {winners.add(player);}}Collections.sort(winners);for (String winner : winners) {System.out.println(winner);}}scanner.close();}private static char getWinner(char move1, char move2) {if ((move1 == 'A' && move2 == 'B') || (move1 == 'B' && move2 == 'C') || (move1 == 'C' && move2 == 'A')) {return move1;} else {return move2;}}
}
Python实现
import sysdef get_winner(move1, move2):if (move1 == 'A' and move2 == 'B') or \(move1 == 'B' and move2 == 'C') or \(move1 == 'C' and move2 == 'A'):return move1else:return move2def main():players = []player_moves = {}moves = set()for line in sys.stdin:line = line.strip()if not line:breakparts = line.split()player_id = parts[0]move = parts[1]players.append(player_id)player_moves[player_id] = movemoves.add(move)# 判断获胜情况if len(moves) == 1 or len(moves) == 3:print("NULL")else:# 两种形状,判断获胜方move_list = list(moves)winning_move = get_winner(move_list[0], move_list[1])# 收集获胜玩家并排序winners = [player for player in players if player_moves[player] == winning_move]winners.sort()for winner in winners:print(winner)if __name__ == "__main__":main()
C++实现
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <algorithm>
#include <string>using namespace std;char getWinner(char move1, char move2) {if ((move1 == 'A' && move2 == 'B') || (move1 == 'B' && move2 == 'C') || (move1 == 'C' && move2 == 'A')) {return move1;} else {return move2;}
}int main() {vector<string> players;map<string, char> playerMoves;set<char> moves;string line;while (getline(cin, line) && !line.empty()) {size_t spacePos = line.find(' ');string playerId = line.substr(0, spacePos);char move = line[spacePos + 1];players.push_back(playerId);playerMoves[playerId] = move;moves.insert(move);}// 判断获胜情况if (moves.size() == 1 || moves.size() == 3) {cout << "NULL" << endl;} else {// 两种形状,判断获胜方vector<char> moveVec(moves.begin(), moves.end());char winningMove = getWinner(moveVec[0], moveVec[1]);// 收集获胜玩家并排序vector<string> winners;for (const string& player : players) {if (playerMoves[player] == winningMove) {winners.push_back(player);}}sort(winners.begin(), winners.end());for (const string& winner : winners) {cout << winner << endl;}}return 0;
}
解法二:直接克制关系判断
思路讲解
这种方法直接根据克制关系来判断:
- 统计每种形状的出现次数
- 检查是否存在一种形状能够克制场上所有其他形状
- 如果存在这样的形状,该形状的玩家获胜;否则平局
这种方法更直观地体现了石头剪刀布的克制关系。
Java实现
import java.util.*;public class RockPaperScissorsV2 {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);List<String> players = new ArrayList<>();Map<String, Character> playerMoves = new HashMap<>();Map<Character, Integer> moveCount = new HashMap<>();String line;while (scanner.hasNextLine() && !(line = scanner.nextLine()).isEmpty()) {String[] parts = line.split(" ");String playerId = parts[0];char move = parts[1].charAt(0);players.add(playerId);playerMoves.put(playerId, move);moveCount.put(move, moveCount.getOrDefault(move, 0) + 1);}// 检查获胜条件char winningMove = findWinningMove(moveCount);if (winningMove == 'X') {System.out.println("NULL");} else {// 收集获胜玩家并排序List<String> winners = new ArrayList<>();for (String player : players) {if (playerMoves.get(player) == winningMove) {winners.add(player);}}Collections.sort(winners);for (String winner : winners) {System.out.println(winner);}}scanner.close();}private static char findWinningMove(Map<Character, Integer> moveCount) {boolean hasA = moveCount.containsKey('A');boolean hasB = moveCount.containsKey('B');boolean hasC = moveCount.containsKey('C');// A克制B,如果有A和B但没有C,A获胜if (hasA && hasB && !hasC) return 'A';// B克制C,如果有B和C但没有A,B获胜if (hasB && hasC && !hasA) return 'B';// C克制A,如果有C和A但没有B,C获胜if (hasC && hasA && !hasB) return 'C';// 其他情况都是平局return 'X';}
}
Python实现
import sys
from collections import defaultdictdef find_winning_move(move_count):has_a = 'A' in move_counthas_b = 'B' in move_counthas_c = 'C' in move_count# A克制B,如果有A和B但没有C,A获胜if has_a and has_b and not has_c:return 'A'# B克制C,如果有B和C但没有A,B获胜if has_b and has_c and not has_a:return 'B'# C克制A,如果有C和A但没有B,C获胜if has_c and has_a and not has_b:return 'C'# 其他情况都是平局return Nonedef main():players = []player_moves = {}move_count = defaultdict(int)for line in sys.stdin:line = line.strip()if not line:breakparts = line.split()player_id = parts[0]move = parts[1]players.append(player_id)player_moves[player_id] = movemove_count[move] += 1# 检查获胜条件winning_move = find_winning_move(move_count)if winning_move is None:print("NULL")else:# 收集获胜玩家并排序winners = [player for player in players if player_moves[player] == winning_move]winners.sort()for winner in winners:print(winner)if __name__ == "__main__":main()
C++实现
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <string>using namespace std;char findWinningMove(const map<char, int>& moveCount) {bool hasA = moveCount.find('A') != moveCount.end();bool hasB = moveCount.find('B') != moveCount.end();bool hasC = moveCount.find('C') != moveCount.end();// A克制B,如果有A和B但没有C,A获胜if (hasA && hasB && !hasC) return 'A';// B克制C,如果有B和C但没有A,B获胜if (hasB && hasC && !hasA) return 'B';// C克制A,如果有C和A但没有B,C获胜if (hasC && hasA && !hasB) return 'C';// 其他情况都是平局return 'X';
}int main() {vector<string> players;map<string, char> playerMoves;map<char, int> moveCount;string line;while (getline(cin, line) && !line.empty()) {size_t spacePos = line.find(' ');string playerId = line.substr(0, spacePos);char move = line[spacePos + 1];players.push_back(playerId);playerMoves[playerId] = move;moveCount[move]++;}// 检查获胜条件char winningMove = findWinningMove(moveCount);if (winningMove == 'X') {cout << "NULL" << endl;} else {// 收集获胜玩家并排序vector<string> winners;for (const string& player : players) {if (playerMoves[player] == winningMove) {winners.push_back(player);}}sort(winners.begin(), winners.end());for (const string& winner : winners) {cout << winner << endl;}}return 0;
}
总结
两种解法都能正确解决问题:
- 解法一通过统计形状种类数量来判断,逻辑简单清晰
- 解法二直接根据克制关系判断,更符合游戏规则的直观理解
时间复杂度都是O(n),空间复杂度也是O(n),其中n是玩家数量。在实际应用中,两种方法都是可行的,可以根据个人喜好选择。