华为OD机试真题——最佳的出牌方法(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
2025 A卷 200分 题型
本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式;
并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析;
本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分享》
华为OD机试真题《最佳的出牌方法》:
文章快捷目录
题目描述及说明
Java
python
JavaScript
C
GO
题目名称:最佳的出牌方法
- 知识点:字符串处理、贪心算法、逻辑分析
- 时间限制:1秒
- 空间限制:256MB
- 语言限制:不限
题目描述
你手上有一副扑克牌(不含大小王),每张牌按牌面数字记分(J=11,Q=12,K=13)。出牌时按照以下规则计算得分:
- 单张牌:得分为牌面数字(如出2得2分)。
- 对子或三张:得分为牌面数字总和×2(如出3张3,得分为(3+3+3)×2=18)。
- 五张顺子:得分为牌面数字总和×2(如出34567,得分为(3+4+5+6+7)×2=50)。
- 顺子需为连续数字(如10JQKA不算顺子)。
- 四张炸弹:得分为牌面数字总和×3(如出4张4,得分为4×4×3=48)。
输入描述:
- 按顺序排列的牌序列,最少1张,最多15张。
- 输入格式:1-9为数字,10输入为0,J/Q/K为大写字母。
- 无需校验非法输入(如非[0-9JQK]字符或某牌超过4张)。
输出描述:
- 最高得分。
示例:
输入:33445677
输出:67
解释:
- 方案1:出对3、对4、对7,单张5、6,得分=(3×2)+(4×2)+(7×2)+5+6=67。
- 方案2:出顺子34567,剩余单张3、4、7,得分=50+(3+4+7)=64。
因此最高得分为67。
Java
问题分析
我们需要计算一副扑克牌的最高得分出牌方案。根据规则,不同的出牌方式有不同的得分计算方式。我们需要考虑所有可能的组合方式,选择得分最高的方案。
解题思路
- 输入处理:将输入的字符转换为对应的牌面数字,统计每张牌的数量。
- 递归遍历所有可能的出牌方式:包括顺子、炸弹、三张、对子、单张。
- 记忆化优化:避免重复计算相同牌数量分布的最高得分。
- 动态规划:通过递归和记忆化,计算所有可能的出牌组合,取最大值。
代码实现
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;public class Main {private static final Map<Long, Integer> memo = new HashMap<>();public static void main(String[] args) {Scanner scanner = new Scanner(System.in);String input = scanner.next();int[] count = new int[14]; // count[1]~count[13] 分别表示牌面1~13的数量for (char c : input.toCharArray()) {int num = convert(c);count[num]++;}System.out.println(dfs(count));}private static int convert(char c) {if (c == '0') return 10; // 输入中的0代表10if (c == 'J') return 11;if (c == 'Q') return 12;if (c == 'K') return 13;return c - '0';}private static int dfs(int[] count) {long code = encode(count);if (memo.containsKey(code)) {return memo.get(code);}int max = calculateSingleSum(count); // 初始值为所有牌作为单张的得分// 处理顺子(五张连续)for (int i = 1; i <= 9; i++) { // 顺子的起始牌最多到9(例如9,10,J,Q,K)if (canFormShunzi(count, i)) {// 扣除顺子的五张牌for (int j = i; j <= i + 4; j++) count[j]--;int current = (i + (i + 1) + (i + 2) + (i + 3) + (i + 4)) * 2 + dfs(count);max = Math.max(max, current);// 恢复for (int j = i; j <= i + 4; j++) count[j]++;}}// 处理炸弹(四张)for (int i = 1; i <= 13; i++) {if (count[i] >= 4) {count[i] -= 4;int current = i * 4 * 3 + dfs(count);max = Math.max(max, current);count[i] += 4;}}// 处理三张for (int i = 1; i <= 13; i++) {if (count[i] >= 3) {count[i] -= 3;int current = i * 3 * 2 + dfs(count);max = Math.max(max, current);count[i] += 3;}}// 处理对子for (int i = 1; i <= 13; i++) {if (count[i] >= 2) {count[i] -= 2;int current = i * 2 * 2 + dfs(count);max = Math.max(max, current);count[i] += 2;}}memo.put(code, max);return max;}// 计算所有剩余牌作为单张的总分private static int calculateSingleSum(int[] count) {int sum = 0;for (int i = 1; i <= 13; i++) {sum += count[i] * i;}return sum;}// 判断是否能组成以start开始的顺子private static boolean canFormShunzi(int[] count, int start) {for (int i = start; i <= start + 4; i++) {if (count[i] < 1) return false;}return true;}// 将count数组编码为唯一的long值(5进制)private static long encode(int[] count) {long code = 0;for (int i = 1; i <= 13; i++) {code = code * 5 + count[i];}return code;}
}
代码详解
- 输入处理:将输入的字符转换为对应的牌面数字,存入数组
count
,其中count[i]
表示数字i的牌的数量。 - 递归函数
dfs
:计算当前牌数量分布下的最高得分。- 记忆化:将当前牌数量编码为
long
值,避免重复计算。 - 顺子处理:遍历所有可能的顺子起始点,扣除牌后递归计算剩余得分。
- 炸弹处理:扣除四张牌,递归计算剩余得分。
- 三张和对子处理:类似炸弹处理方式。
- 记忆化:将当前牌数量编码为
- 单张得分计算:当无法组成其他组合时,所有剩余牌作为单张计算得分。
示例测试
示例1:
输入:33445677
输出:67
解析:最佳方案为拆分为对子3、4、7,单张5、6,得分67。