引题:

背景:判断和牌

SA酱开始喜欢上了打麻将,但是他是新手,没办法一眼看出牌和没和,所以想要你帮助他写出一段代码,判断这18张牌和没和牌。

输入格式:

一行,输入14张牌牌面的代码,不同牌之间用空格隔开。牌的顺序是随机的。

每张牌面对应一个由两个字符组成的代码,具体规则如下(共34种牌,每一种牌各4张):

万子牌9种,代码为格式 "数字 + 大写M",例如五万的代码是"5M";

筒子牌9种,代码格式为 "数字 + 大写P", 例如八筒的代码是"8P";

索子牌9中,代码格式为 "数字 + 大写S", 例如三索的代码是 "3S";

四种风牌的代码为 "数字 + 大写W",其中东 南 西 北各表示1 2 3 4,例如南风的代码是 "2W";

三元牌的代码为 “数字 + 大写Z”, 其中 白 发 中 各表示1 2 3,例如 发 的代码是 "2Z"

输出格式:

如果牌为可以和的牌,则输出 "Yes"; 否则输出 "No"。包含七对子的情况、不包含国士无双(十三幺)的情况。

 

麻将和牌的方式:

正常和牌方式:由四个面子 + 一个雀头组成。

面子包含刻子、顺子和杠子,刻子为三张完全相同的牌,例如1s1s1s;顺子为三个相同花色相临的三张不同的牌,例如1s2s3s,其中风牌、三元牌不能做顺子;由于牌数固定,本题肯定是不考虑杠的情况。

雀头由一个对子组成,即两张相同的牌,例如1s1s

七对子和牌:由七个对子组成

基本思路:

一、排序 + 预处理

由于输入的顺序是随机的,所以第一步想的方法肯定是排序

因为输入格式都是采用 数字 + 字母 的形式,其实可以转化为全数字的形式,根据麻将的规则,可以将不能互相组成顺子的牌之间隔开一个数字

例如万子牌可以表示为 1~9,筒子牌可以表示为 11~19, 索子牌可以表示为 21~29。由于字牌不能组成顺子,所以可以将东 南 西 北分别由30 32 34 36表示,白 发 中分别由38 40 42表示。

这样就可以直接用从小到大 sort 来对牌面进行排序了。

二、判断

麻将和牌的方式是由四个面子+一个雀头组成,所以我们需要判断这些牌是否能组成到四个面子+一个雀头,这两种牌型因为格式不同应该分别判断。

很明显雀头比起面子更容易判断,更重要的因素是雀头只有一个,我们可以先判断输入的牌是否存在两张相同的牌,如果存在则依次去掉这两个相同的牌,再判断剩下的牌是否存在四个面子。如果不存在两张相同的牌,那么一定不可能和牌。那么剩下的就需要判断12张牌中是否存在四个面子就行了。假设这十二张已经排序的牌的编号为1~12(不是牌面代码)

判断雀头:

很简单,略

判断面子:

我们可以知道,对于每一张牌,要么组成刻子要么组成顺子。有以下几种规律:

1. 如果不考虑做雀头的一组12张牌(也就是这12张牌都不做雀头中最小的牌可以组成刻子,那么这个牌无论是去组成刻子还是组成顺子都对整个牌面是否和无影响,直接取作为刻子即可。

关于为什么可以直接组成刻子:一般这种情况拿九莲宝灯去举例子是最好的,假设九宝莲灯胡2,也就是 11122345678999,这种情况是只能拿111当刻子才能和的;假设胡3,也就是 11123345678999,这种情况如果不拿11做雀头的话,可以发现1无论是111形成刻子还是123形成顺子都无法和;如果拿11做雀头的话,那么就不符合“不考虑雀头的一组12张牌”的前提了。

为什么是最小的牌:一个典型的例子就是344455566,组成的面子应该为 3 4 5| 4 5 6 | 4 5 6,如果拿444、555组成刻子的话会被判断为无法和。

作为刻子后,删除这三张牌,重新从最小的牌开始判断。

2. 如果最小的牌不能组成刻子,那么这个牌只能组成顺子;判断顺子的方法:一个指针从第二个牌开始指向,如果上一张牌的牌面等于当前指的牌则指向下一张牌;如果上一张牌的牌面等于当前只的牌牌面-1,那么就加入到顺子的行列;如果上一张牌面既不等于当前的牌面,也不等于当前牌面-1,那么直接退出报No。删除组成这顺子的三张牌,重新从最小的牌开始判断。

 

待补充:听牌判断、向听判断、进张判断