麻将
* * * *
拉格朗日计划
* * * *
麻将

给定一系列麻将的手牌,若能在日本麻将规则下胡牌,就将其打印出来。

麻将牌由序数牌和字牌组成,每种牌都有四张。

序数牌有“筒”、“万”、“条”三类,每类都包括1到9九种牌。

字牌包括“东南西北”四种风向和“中发白”三种箭牌。

              筒: 🀙🀚🀛🀜🀝🀞🀟🀠🀡

              万: 🀇🀈🀉🀊🀋🀌🀍🀎🀏

              条: 🀐🀑🀒🀓🀔🀕🀖🀗🀘

              字: 🀀🀁🀂🀃🀄🀅🀆
              
刻子是指三张一样的牌,例如 🀓🀓🀓 或 🀅🀅🀅。

顺子是指三张类型一样,且数字从小到大严格增加一的序数牌,例如 🀇🀈🀉 或 🀜🀝🀞。

一副顺子或一副刻子统称为一坎。

将牌是指两张一样的牌,即对子。

日本麻将有以下三种胡法:

              十三幺:东、南、西、北、中、发、白、一万、九万、一筒、九筒、一条、九条各一张,且手牌中能形成一副将牌。

              七对子:七副不同的将牌。注意四张相同的牌不能视作两对。

              垃圾胡:四坎+一副将牌。
              
例如 🀇🀈🀈🀈🀉🀓🀔🀕🀚🀛🀜🀀🀀🀀 能分割成 🀇🀈🀉 🀓🀔🀕 🀚🀛🀜 🀀🀀🀀 四坎 和 🀈🀈 一副将牌,因此能作垃圾胡。

输入的数据是一系列由14个上述表示麻将牌的Unicode字符组成的字符串列表,每个字符串表示一副手牌,手牌中不会包含4张一样的牌(即不含“杠”的情况)。

若一副手牌能在日本麻将规则下胡牌,就将其打印出来,打印的顺序需要与输入的顺序一致。

本题难度:



解答

非常繁琐的题,先判断十三幺和七对子。

十三幺:要求的牌面已经有十三张,因此将牌只能是这十三种之一,所以当且仅当手牌转化为集合后与这十三种牌组成的集合一致时才能作十三幺。

七对子:当且仅当每张手牌的出现次数都是2时才能作七对子。

接下来递归判断垃圾胡:

把手牌排序,每次考察最小的牌:

若当前牌数不能被3整除,说明还未取过将牌,若最小的牌可以形成将牌,就把这一对牌移除并考察剩余的牌。

否则若能形成一坎,就将这一坎移除再考察剩余的牌。

其中刻子较易识别和处理。要识别顺子,可以先生成以第一张牌为键值的字典辅助判断。

最后,若所有牌都能在上述过程中移除,说明可以胡牌,否则不能胡牌。

最终代码行有五行。

代码长度:523字节 vs. 全站第一:198字节。

import sys
d={k[i]:k[i:i+3]for i in range(7)for k in["🀇🀈🀉🀊🀋🀌🀍🀎🀏","🀙🀚🀛🀜🀝🀞🀟🀠🀡","🀐🀑🀒🀓🀔🀕🀖🀗🀘"]}
x=lambda s:(len(s)%3and s[0]==s[1]and x(s[2:]))or(s[0]==s[1]==s[2]and x(s[3:]))or(s[0]in d and {*d[s[0]]}<={*s}and x([j for i,j in enumerate(s)if i>0and(j not in d[s[0]]or j==s[i-1])]))if s else 1
for a in sys.argv[1:]:(all([a.count(i)==2 for i in a])or {*"🀀🀁🀂🀃🀄🀅🀆🀇🀏🀐🀘🀙🀡"}=={*a}or x(sorted(list(a))))and print(a)