Solution -「GXOI / GZOI 2019」宝牌一大堆

cirnovsky /

§ Description

Link.

Summarizing the fucking statement is the last thing in the world I ever want to do.

§ Solution

我们来重新描述一些概念:

  • 数字牌:筒条万。
  • 顺子:33 张连续的数字牌。(面子)
  • 雀头:22 张完全一样的牌。
  • 刻子:33 张完全一样的牌。(面子)
  • 杠子:44 张完全一样的牌。

观察发现:杠子在除了 1414 张牌胡牌情况以外的胡牌情况都出现了,于是又发现:(43)>(44)\binom{4}{3}>\binom{4}{4}

于是:

  • 对于胡牌的形式,我们只需要考虑「3×4+23\times4+2」「七对子」和「国士无双」。

于是我们只需要三种情况取最大即可。

  1. 「七对子」只需要把所有牌型的贡献拉出来,取前 77 个最大的乘起来即可。

  2. 「国士无双」枚举谁来做 22 个的那个,然后取最大。

  3. 3×4+23\times4+2

\qquad 考虑这样一个 DP,设:f(i,j,k,a,b,c)f(i,j,k,a,b,c) 为前 ii 个数,jj 个面子,kk 个雀头,第 ii / i+1i+1 / i+2i+2 张牌用了 aa / bb / cc 张的最优答案(k{0,1}k\in\{0,1\})。

\qquad \qquad 1. 对于雀头:

f(i,j,1,a+2,b,c)=max{f(i,j,0,a,b,c)×calc(i,a+2)calc(i,a)}f(i,j,1,a+2,b,c)=\max\{\frac{f(i,j,0,a,b,c)\times\text{calc}(i,a+2)}{\text{calc}(i,a)}\}

\qquad \qquad \qquad 其中 calc(x,y)\text{calc}(x,y) 为第 xx 张牌,在手牌中需要出现 yy 次,此时对答案的贡献。

\qquad \qquad \qquad 方程的意义即:去掉把第 ii 种牌之前的贡献,再算上现在算作雀头的贡献。

\qquad \qquad 2. 对于刻子:

f(i,j+1,0 or 1,a+3,b,c)=max{f(i,j,0 or 1,a,b,c)×calc(i,a+3)calc(i,a)}f(i,j+1,0\text{ or }1,a+3,b,c)=\max\{\frac{f(i,j,0\text{ or }1,a,b,c)\times\text{calc}(i,a+3)}{\text{calc}(i,a)}\}

\qquad \qquad \qquad 基本同上。

\qquad \qquad 3. 对于顺子:

f(i,j+1,0 or 1,a+1,b+1,c+1)=max{f(i,j,0 or 1,a,b,c)×calc(i,a+1)×calc(i+1,b+1)×calc(i+2,c+1)calc(i,a)×calc(i+1,b)×calc(i+2,c)}f(i,j+1,0\text{ or }1,a+1,b+1,c+1)=\max\{\frac{f(i,j,0\text{ or }1,a,b,c)\times\text{calc}(i,a+1)\times\text{calc}(i+1,b+1)\times\text{calc}(i+2,c+1)}{\text{calc}(i,a)\times\text{calc}(i+1,b)\times\text{calc}(i+2,c)}\}

\qquad \qquad \qquad 完全同理。

然后就完了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL koshi[15]={0,1,9,10,18,19,27,28,29,30,31,32,33,34}; // the cards that Kokushimusou needs
const bool chunko[36]={0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1}; // the cards which are able to be jyunko
LL getCard()
{
	static char s[10];
	scanf("%s",s+1);
	if(s[1]=='0')	return -1;
	else if(!isdigit(s[1]))
	{
		if(s[1]=='E')	return 28;
		else if(s[1]=='S')	return 29;
		else if(s[1]=='W')	return 30;
		else if(s[1]=='N')	return 31;
		else if(s[1]=='Z')	return 32;
		else if(s[1]=='B')	return 33;
		else	return 34;
	}
	else
	{
		if(s[2]=='m')	return s[1]-'0';
		else if(s[2]=='p')	return s[1]-'0'+9;
		else	return s[1]-'0'+18;
	}
}
LL t,comb[10][10],cnt[40],trs[40],f[40][5][5][5][5][5];
#define calc(x,f) (((cnt[x])<(f)?0:comb[cnt[x]][f])*(trs[x]?(1<<(f)):1))
LL onesolve() // Seven Pairs
{
	vector<LL> pri;
	for(LL i=1;i<=34;++i)	if(cnt[i]>=2)	pri.push_back(calc(i,2));
	sort(pri.begin(),pri.end(),greater<LL>());
	if(pri.size()<size_t(7))	return 0;
	else
	{
		LL res=7;
		for(LL i=0;i<7;++i)	res*=pri[i];
		return res;
	}
}
LL anosolve() // Kokushimusou
{
	LL flag=0;
	for(LL i=1;i<=13;++i)
	{
		if(cnt[koshi[i]]>=2)	flag=1;
		if(cnt[koshi[i]]==0)	return 0;
	}
	if(flag)
	{
		LL res=0;
		for(LL i=1;i<=13;++i)
		{
			LL tmp=13;
			if(cnt[koshi[i]]>=2)
			{
				for(LL j=1;j<=13;++j)	tmp*=calc(koshi[j],(i==j)+1);
			}
			res=max(res,tmp);
		}
		return res;
	}
	else	return 0;
}
void getmax(LL &one,LL ano){one=max(one,ano);}
LL exsolve() // 3x4+2
{
	#define f(i,j,k,a,b,c) (f[i][j][k][a][b][c])
	f(1,0,0,0,0,0)=1;
	for(LL i=1;i<=34;++i)
	{
		for(LL j=0;j<=4;++j)
		{
			for(LL a=0;a<=4;++a)
			{
				for(LL b=0;b<=2;++b)
				{
					for(LL c=0;c<=2;++c)
					{
						if(cnt[i]-a>=2)	getmax(f(i,j,1,a+2,b,c),f(i,j,0,a,b,c)/calc(i,a)*calc(i,a+2));
						if(j^4)
						{
							if(cnt[i]-a>=3)
							{
								getmax(f(i,j+1,0,a+3,b,c),f(i,j,0,a,b,c)/calc(i,a)*calc(i,a+3));
								getmax(f(i,j+1,1,a+3,b,c),f(i,j,1,a,b,c)/calc(i,a)*calc(i,a+3));
							}
							if(chunko[i]&&cnt[i]-a>=1&&cnt[i+1]-b>=1&&cnt[i+2]-c>=1&&(b^2)&&(c^2))
							{
								getmax(f(i,j+1,0,a+1,b+1,c+1),f(i,j,0,a,b,c)/calc(i,a)/calc(i+1,b)/calc(i+2,c)*calc(i,a+1)*calc(i+1,b+1)*calc(i+2,c+1));
								getmax(f(i,j+1,1,a+1,b+1,c+1),f(i,j,1,a,b,c)/calc(i,a)/calc(i+1,b)/calc(i+2,c)*calc(i,a+1)*calc(i+1,b+1)*calc(i+2,c+1));
							}
						}
						getmax(f(i+1,j,0,b,c,0),f(i,j,0,a,b,c));
						getmax(f(i+1,j,1,b,c,0),f(i,j,1,a,b,c));
					}
				}
			}
		}
	}
	LL res=0;
	for(LL i=1;i<=34;++i)
	{
		for(LL a=0;a<=4;++a)
		{
			for(LL b=0;b<=2;++b)
			{
				for(LL c=0;c<=2;++c)	getmax(res,f(i,4,1,a,b,c));
			}
		}
	}
	#undef f
	return res;
}
int main()
{
	for(LL i=0;i<=4;++i)	comb[i][0]=comb[i][i]=1;
	for(LL i=1;i<=4;++i)	for(LL j=1;j<i;++j)	comb[i][j]=comb[i-1][j-1]+comb[i-1][j];
	scanf("%lld",&t);
	while(t--)
	{
		memset(f,0,sizeof(f));
		for(LL i=1;i<=34;++i)	cnt[i]=4,trs[i]=0;
		LL tmp=getCard();
		while(~tmp)	--cnt[tmp],tmp=getCard();
		tmp=getCard();
		while(~tmp)	trs[tmp]=1,tmp=getCard();
		printf("%lld\n",max(max(onesolve(),anosolve()),exsolve()));
	}
	return 0;
}