传送门
其实还有第三种麻将——四川麻将,规则和其他麻将都不太像,甚至在四川不同地区的规则也不一样。
而关于麻将胡牌的题,能够利用DP解决的,我们可以将这种技巧成为麻将DP
题解:
这篇题解里面用的是川麻的语言习惯。
首先注意到杠子是废的,我们永远不可能考虑用一个杠子代替一个刻子,因为它权值永远小于刻子。(没注意到没关系,反正写到DP里面杠子的转移就一行)
国士无双可以直接枚举重复的那个是什么 O ( 1 3 3 ) O(13^3) O(133)暴力算。
七对子显然直接选权值最大的7个就行了。
那么考虑普通胡牌。
同类型的顺子不可能出现两次以上,如果出现了三次,我们可以用三个刻子代替。
那么DP状态就很好设计了:
设 f [ i ] [ j ] [ k ] [ l ] [ 0 / 1 ] f[i][j][k][l][0/1] f[i][j][k][l][0/1]表示当前考虑了前 i i i种牌,已经确定组成的面子有 j j j个,以 i − 1 i-1 i−1开头的顺子有 k k k个,以 i i i开头的顺子有 l l l个(注意这两种顺子是未确定的),是否包含一个对子,的最大权值。
转移大力分类讨论,注意不要把不该顺上的牌连上了。
代码里面的两个函数国士无双和七对子用的是日语罗马音。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cin;
using std::cout;
using std::cerr;
inline void ckmax(ll &a,ll b){a=a<b?b:a;}
cs ll c[5][5]={
{1},
{1,1},
{1,2,1},
{1,3,3,1},
{1,4,6,4,1}
};
/*
万子:1~9
筒子:10~18
条子:19~27
东西南北中白发:28~34;
*/
cs int kokushi[]={1,9,10,18,19,27,28,29,30,31,32,33,34};
ll a[39],b[39];
ll f[39][5][4][4][2];
inline ll kokushimuso(){
for(int re i:kokushi)if(a[i]==0)return 0;
ll ans=0,tmp;
for(int re i:kokushi){
if(a[i]<2)continue;tmp=1;
for(int re j:kokushi)
if(i==j)tmp*=c[a[j]][2]*b[j]*b[j];
else tmp*=c[a[j]][1]*b[j];
ckmax(ans,tmp);
}
return ans*13;
}
inline ll nanakumi(){
static int val[34],cnt;
cnt=0;
for(int re i=1;i<=34;++i)
if(a[i]>1)val[++cnt]=c[a[i]][2]*b[i]*b[i];
if(cnt<7)return 0;ll ans=1;
std::sort(val+1,val+cnt+1);
for(int re i=cnt;i>cnt-7;--i)ans*=val[i];
return ans*7;
}
inline ll dp(){
memset(f,0,sizeof f);
f[0][0][0][0][0]=1;
for(int re i=0;i<34;++i)
for(int re j=0;j<=4;++j)
for(int re k=0;k<3&&k+j<=4;++k){
if(k&&(i==9||i==18||i>=27))break;
for(int re l=0;l<3&&l+k+j<=4;++l){
if(l&&(i==9||i==18||i>=27))break;
if(!f[i][j][k][l][0]&&!f[i][j][k][l][1])continue;
ll v0=f[i][j][k][l][0],v1=f[i][j][k][l][1];
for(int re t=0;t<=a[i+1]-k-l;++t){
int tot=t+k+l;
ll tr=c[a[i+1]][tot]*(b[i+1]==2?1ll<<tot:1);
if(j+tot<=4&&t<3){
ckmax(f[i+1][j+k][l][t][0],v0*tr);
ckmax(f[i+1][j+k][l][t][1],v1*tr);
}
if(t>=2&&j+tot-2<=4){
ckmax(f[i+1][j+k][l][t-2][1],v0*tr);
}
if(t>=3&&j+tot-2<=4){
ckmax(f[i+1][j+k+1][l][t-3][0],v0*tr);
ckmax(f[i+1][j+k+1][l][t-3][1],v1*tr);
}
}
}
}
return f[34][4][0][0][1];
}
signed main(){
#ifdef zxyoi
freopen("kokushi.in","r",stdin);
#endif
std::ios::sync_with_stdio(false);
int T;cin>>T;
std::string s;
while(T--){
for(int re i=1;i<=34;++i)a[i]=4,b[i]=1;
while(true){
cin>>s;
if(s.size()==1){
if(s[0]=='0')break;
switch(s[0]){
case 'E':--a[28];break;
case 'S':--a[29];break;
case 'W':--a[30];break;
case 'N':--a[31];break;
case 'Z':--a[32];break;
case 'B':--a[33];break;
case 'F':--a[34];break;
}
}
else switch(s[1]){
case 'm':--a[s[0]-'0'];break;
case 'p':--a[s[0]-'0'+9];break;
case 's':--a[s[0]-'0'+18];break;
}
}
while(true){
cin>>s;
if(s.size()==1){
if(s[0]=='0')break;
switch(s[0]){
case 'E':b[28]=2;break;
case 'S':b[29]=2;break;
case 'W':b[30]=2;break;
case 'N':b[31]=2;break;
case 'Z':b[32]=2;break;
case 'B':b[33]=2;break;
case 'F':b[34]=2;break;
}
}
else switch(s[1]){
case 'm':b[s[0]-'0']=2;break;
case 'p':b[s[0]-'0'+9]=2;break;
case 's':b[s[0]-'0'+18]=2;break;
}
}
ll ans=std::max(kokushimuso(),nanakumi());
ckmax(ans,dp());
cout<<ans<<"\n";
}
return 0;
}