1 条题解
-
0
题目大意
给定已经打出的牌和宝牌列表,求在所有尚未打出的牌中,能够组成的和牌型手牌中「达成分数」的最大值。
和牌型包括:
- 普通和牌()
- 七对子
- 国士无双(十三幺)
- 含杠子的和牌(- 张牌)
达成分数计算:
- 基本分 = 选择剩余牌组成手牌的组合数
- 乘上
- 七对子额外乘 ,国士无双额外乘
问题分析
本题是一个麻将牌型枚举 + 组合计数问题,难点在于:
- 状态空间巨大: 种牌,每种 - 张,状态数为 ,不可直接枚举
- 多种和牌型:需要分别处理普通型、七对子、国士无双
- 组合数学计算:对于每种可能的手牌,要计算其达成方案数
解题思路
1. 数据预处理
- 将 种牌编号(-)
- 统计每种牌的剩余数量:
- 标记哪些牌是宝牌
2. 分别计算三种和牌型的最高分
(1) 国士无双(十三幺)
- 条件:包含 种幺九字牌各至少 张,且总牌数 张
- 枚举:哪张牌多 张作为雀头
- 分数计算:
- 对于 种牌,选择至少 张:
- 对于多 张的那张牌:乘
- 乘 ,再乘
(2) 七对子
- 条件: 个不同的对子
- 枚举:选择 种牌,每种牌取 张
- 分数计算:
- (选择 种牌)
- 乘 ,再乘
(3) 普通和牌()
这是最复杂的部分,采用DP搜索:
DP状态设计:
- 按万、筒、索、字牌的顺序处理
- 状态: 表示:
- 处理到第 种牌
- 已有 个面子
- 已有 个杠子
- 前面残留 张牌需要顺子
- 前面残留 张牌需要顺子
- 当前是否有雀头()
状态转移: 对于第 种牌,枚举取 张():
- 组成刻子:
- 组成杠子:
- 组成雀头: 且之前无雀头
- 组成顺子:数字牌可向前面的残留牌组成顺子
分数计算: 当 ( 面子)且有雀头时,计算当前方案的分数:
- 组合数:
- 宝牌倍率:
3. 含杠子的和牌
通过调整DP状态中的面子数和杠子数来处理:
- 张: 面子 + 杠子 + 雀头
- 张: 面子 + 杠子 + 雀头
- 张: 面子 + 杠子 + 雀头
- 张: 面子 + 杠子 + 雀头
算法优化
- 状态剪枝:面子数 、杠子数 、残留牌 的状态直接舍弃
- 牌型分组:万、筒、索、字牌分开处理,字牌不能组成顺子
- 记忆化搜索:对每个花色进行DP,减少状态数
- 组合数预处理:预计算所有
复杂度分析
- 国士无双: 枚举
- 七对子: 但可用堆优化到
- 普通和牌:DP状态数约 ,可接受
代码实现要点
// 伪代码框架 vector<int> remain(34, 4); // 每种牌剩余数量 vector<bool> dora(34, false); // 宝牌标记 // 1. 读入数据,更新remain和dora // 2. 计算国士无双分数 ans = max(ans, kokushi()); // 3. 计算七对子分数 ans = max(ans, seven_pairs()); // 4. 计算普通和牌分数 DP状态初始化 for 每种花色: 进行DP转移 ans = max(ans, 普通和牌最高分) // 5. 输出答案
总结
本题综合了状态DP、组合数学、麻将规则等多种知识点,需要:
- 深入理解麻将的各种和牌型
- 设计合适的状态表示和转移方程
- 高效处理组合计数和宝牌倍率
通过分别处理三种和牌型,并利用DP搜索普通和牌,可以在合理时间内求出最高达成分数。
- 1
信息
- ID
- 4367
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者