时间: 1000ms / 空间: 131072KiB / Java类名: Main
描述
麻将是中国传统的娱乐工具之一。麻将牌的牌可以分为字牌(共有东、南、西、北、中、发、白七种)和序数牌(分为条子、饼子、万子三种花色,每种花色各有一到九的九种牌),每种牌各四张。在麻将中,通常情况下一组和了的牌(即完成的牌)由十四张牌组成。十四张牌中的两张组成对子(即完全相同的两张牌),剩余的十二张组成三张一组的四组,每一组须为顺子(即同花色且序数相连的序数牌,例如条子的三、四、五)或者是刻子(即完全相同的三张牌)。一组听牌的牌是指一组十三张牌,且再加上某一张牌就可以组成和牌。那一张加上的牌可以称为等待牌。
在这里,我们考虑一种特殊的麻将。在这种特殊的麻将里,没有字牌,花色也只有一种。但是,序数不被限制在一到九的范围内,而是在1到n的范围内。同时,也没有每一种牌四张的限制。一组和了的牌由3m + 2张牌组成,其中两张组成对子,其余3m张组成三张一组的m组,每组须为顺子或刻子。现给出一组3m + 1张的牌,要求判断该组牌是否为听牌(即还差一张就可以和牌)。如果是的话,输出所有可能的等待牌。
输入格式
输入文件包含两行。
第一行包含两个由空格隔开整数n, m (9<=n<=400, 4<=m<=1000)。第二行包含3m + 1个由空格隔开整数,每个数均在范围1到n之内。这些数代表要求判断听牌的牌的序数。
输出格式
输出为一行。
如果该组牌为听牌,则输出所有的可能的等待牌的序数,数字之间用一个空格隔开。所有的序数必须按从小到大的顺序输出。如果该组牌不是听牌,则输出"NO"。
测试样例1
输入
9 4
1 1 2 2 3 3 5 5 5 7 8 8 8
输出
6 7 9
【解题思路】
一、枚举胡的牌,枚举哪个当一对,从左到右简单贪心扫描一遍即可,贪心是指可以取刻子的就取刻子,剩下的去顺子,O(n3)
二、枚举胡的牌,f[I,p,j,k]:i=0表示已找到了对子,i=1表示没找到,已计算了前p位,p+1等价于有j张牌,p+2等价于有k张牌的情况是否可能出现。 O(n2)
三、f[I,o,p,j,k]:o=0表示加了要胡的牌,o=1表示没加要胡的牌,i=0表示已找到了对子,i=1表示没找到,已计算了前p位,p+1等价于有j张牌,p+2等价于有k张牌的情况是否可能出现。 O(n)
【分析】
看到题我首先想到的是枚举,我们可以依次枚举每个数字,即要听的牌,看加上这个数字之后能不能和,能的话记录下来,不能的话继续枚举下一个数字。那问题就在于如何来判断加上枚举的牌能否和。
首先,在我们判断的时候,每个数字只有两种情况:
1、它是刻子(即它有三个)
2、它是顺子的一个(此处我们直接判断第一个即可)
然后在我们判断这种情况能否成立的时候,首先我们应该先枚举找将(也就是那个对子),找到将之后,去掉将(把这两个数的次数减掉),然后在这个将的情况下去从前往后枚举每一个数字,对每个数字进行判断是否满足上面两种情况。这样的话,第一种很简单(a[i]:=a[i] mod 3;),我们只需看第二种即可,满足的话减掉这三个数的次数,如果已经是倒数第二个数或者最后一个,后面已经不能有两个数了,但如果此时它仍然不为0的话证明肯定胡不了,所以退出,枚举下一个麻将。
最后如果没有数字能成立的话输出no,能成立的话输出能听的牌。枚举完听牌之后输出。
【代码详解】
var
a,b,w:array[1..400]of longint;
m,n,t,i,j,k,tot:longint;
ok:boolean;
begin
readln(n,m);
fori:= 1 to m*3+1 do
begin
read(t);
inc(a[t]); //a数组用来记录每个数出现的次数
end;
tot:=0;
fori:= 1 to n do //枚举听牌
begin
inc(a[i]); //把枚举的数加上
for j:= 1 to n do
if a[j]>=2 then //枚举将
begin
b:=a; //我们用数组b来判断,防止a数组弄乱
b[j]:=b[j]-2; //减去将
ok:=true; //先将ok的值记为true
for k:= 1 to n do //从头到尾来模拟能否胡
begin
b[k]:=b[k] mod 3;//减掉刻子
if (k+2>n)and(b[k]<>0) then{k+2>n证明这已经是倒数第二个数或者最后一个,后面已经不能有两个数了,}
begin
ok:=false; //但如果此时它仍然不为0的话证明肯定胡不了
break; //胡不了所以退出,来枚举另一个将
end;
if (b[k+1]<b[k])or(b[k+2]<b[k]) then//后面两个数的次数任何一个比它大都不可能胡
begin
ok:=false;
break;
end;
b[k+1]:=b[k+1]-b[k];
b[k+2]:=b[k+2]-b[k];
end;
if ok then break;
end;
dec(a[i]);//把刚刚枚举的听牌还原
if ok then //如果能胡的话记录
begin
inc(tot);
w[tot]:=i;
end;
end;
iftot=0 then writeln(‘NO‘) else
begin
fori:= 1 to tot-1 do
write(w[i],‘ ‘);
writeln(w[tot]);
end;
end.
版权声明:本文为博主原创文章,未经博主允许不得转载。