其他分享
首页 > 其他分享> > 【题解】[GXOI/GZOI2019]宝牌一大堆

【题解】[GXOI/GZOI2019]宝牌一大堆

作者:互联网

[GXOI/GZOI2019]宝牌一大堆

\(\text{Solution:}\)

从 \(8kb\) 代码改到 \(11kb\) 最后封装到 \(5kb\) ……封装 yyds dwt yyds

学到最大的除了 \(dp\) 应该是调试技巧和封装的重要性了……

方程可能写的有点奇怪)看看能不能帮到和我一样设计状态的同学)

后面附带了一些注意事项。欢迎补充。

考虑枚举哪一张牌选两张即可,复杂度 \(O(13^2).\)

由于七对子不能重复,所以考虑把每一张牌做对子的价值倍率都记录下来,选取最大的七个即可。

这部分就需要 \(dp\) 了,暴力的复杂度显然不能接受。

考虑:设 \(dp[i][j][k][l][m]\) 表示当前是第 \(i\) 张牌,已经凑出 \(j\) 个面子, \(k\) 代表有没有雀头, 当前牌还剩 \(l\) 张,上一张牌还剩 \(m\) 张的最大得分。

注意这里是还剩下几张,所以转移的时候是用 \(i+1\) 牌的数量减去当前转移用掉的牌数。

以下令函数 \(\text{Getv(pos,num)}\) 表示第 \(pos\) 张牌有 \(num\) 张造成的宝牌收益。

凑一组刻子:

条件: \(Num_{i+1}>2\)

转移:

\[dp[i+1][j+1][k][Num_{i+1}-3][l]=\max\left\{ dp[i][j][k][l][m]\cdot Getv(i+1,3)\cdot {Num_{i+1}\choose 3}\right\} \]

凑一组顺子:

条件: \(l>0,m>0,Num_{i+1}>0\)

转移:

\[dp[i+1][j+1][k][Num_{i+1}-1][l-1]=\max\left\{ dp[i][j][k][l][m]\cdot Getv(i-1,1)\cdot Getv(i,1)\cdot Getv(i+1,1)\cdot\frac{ {Num_{i-1}\choose Num_{i-1}-m+1}\cdot {Num_i\choose Num_i-l+1}\cdot{Num_{i+1}\choose 1}}{{Num_i\choose l}\cdot {Num_{i-1}\choose m}}\right\} \]

凑两组顺子:

条件: \(l>1,m>1,Num_{i+1}>1\)

转移:

\[dp[i+1][j+2][k][Num_{i+1}-2][l-2]=\max\left\{ dp[i][j][k][l][m]\cdot Getv(i-1,2)\cdot Getv(i,2)\cdot Getv(i+1,2)\cdot\frac{ {Num_{i-1}\choose Num_{i-1}-m+2}\cdot {Num_i\choose Num_i-l+2}\cdot{Num_{i+1}\choose 2}}{{Num_i\choose l}\cdot {Num_{i-1}\choose m}}\right\} \]

凑一组雀头:

条件: \(k=0,Num_{i+1}>1\)

转移:

\[dp[i+1][j][k+1][Num_{i+1}-2][l]=\max\left\{dp[i][j][k][l][m]\cdot{Num_{i+1}\choose 2}\cdot Getv(i+1,2)\right\} \]

凑一组雀头带顺子一对:

条件: \(k=0,Num_{i+1}>2,l>0,m>0\)

转移:

\[dp[i+1][j+1][k+1][Num_{i+1}-3][l-1]=\max\left\{dp[i][j][k][l][m]\cdot Getv(i-1,1)\cdot Getv(i,1)\cdot Getv(i,3)\cdot\frac{{Num_{i+1}\choose 3}\cdot{Num_i\choose Num_i-l+1}\cdot {Num_{i-1}\choose Num_{i-1}-m+1}}{{Num_i\choose l}\cdot {Num_{i-1}\choose m}}\right\} \]

凑一组雀头带顺子两对:

条件: \(k=0,l>1,m>1,Num_{i+1}=4\)

转移:

\[dp[i+1][j+2][k+1][Num_{i+1}-4][l-2]=\max\left\{dp[i][j][k][l][m]\cdot Getv(i-1,2)\cdot Getv(i,2)\cdot Getv(i,4)\cdot\frac{{Num_{i+1}\choose 4}\cdot{Num_i\choose Num_i-l+2}\cdot {Num_{i-1}\choose Num_{i-1}-m+2}}{{Num_i\choose l}\cdot {Num_{i-1}\choose m}}\right\} \]

凑顺子带刻子:

条件: \(Num_{i+1}=4,l>0,m>0\)

转移:

\[dp[i+1][j+2][k][Num_{i+1}-4][l-1]=\max\left\{dp[i][j][k][l][m]\cdot Getv(i+1,4)\cdot Getv(i,1)\cdot Getv(i-1,1)\cdot\frac{{Num_i\choose Num_i-l+1}\cdot {Num_{i-1}\choose Num_{i-1}-m+1}\cdot{Num_i\choose 4}}{{Num_i\choose l}\cdot{Num_{i-1}\choose m}}\right\} \]

不选当前牌:

转移:

\[dp[i+1][j][k][Num_{i+1}][l]=\max\left\{dp[i][j][k][l][m]\right\} \]

注意事项:

代码:

#include<bits/stdc++.h> 
using namespace std;
#define int long long
int T,fac[20],card,num[50],vis[50];
vector<int>v;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
void pre(){
	fac[0]=1;card=0;
	for(int i=1;i<=12;++i)fac[i]=fac[i-1]*i;
	for(int i=1;i<=34;++i)num[i]=4;
	for(int i=1;i<=34;++i)vis[i]=0;
}
int C(int x,int y){if(x<y)return 1;return fac[x]/fac[y]/fac[x-y];}
// num是牌的数量 vis是标记是不是宝牌 fac[]是阶乘 card是总牌数 
/*--------国士无双-----------*/
inline bool check_Tbp(int p){
	if(vis[p]&&num[p]>1)return true;
	return false;
}
int equal(){
	int ct=0;
	for(auto j:v){
		if(num[j]<1)return 0;
		ct+=num[j];
	}
	int ans=-1;
	for(auto j:v){
		if(num[j]<2)continue;
		// cout<<j<<" "<<num[j]<<" "<<vis[j]<<endl;
		int res=C(num[j],2);
		if(vis[j])res<<=2ll;
		for(auto k:v){
			if(k==j)continue;
			res*=num[k];
			if(vis[k])res<<=1ll;
		}
		ans=Max(ans,res);
//		if(res==ans)cout<<j<<" "<<num[j]<<endl;
	}
	return ans*13ll;
}
/*-------------七对子-------------*/
int val[50];
inline bool cmp(int A,int B){return A>B;}
int solve_seven(){
	for(int i=1;i<=34;++i){
		if(num[i]<2)continue;
		val[i]=C(num[i],2);
		if(vis[i])val[i]<<=2;
	}
	sort(val+1,val+34+1,cmp);
	int ans=1;
	for(int i=1;i<=7;++i)ans=ans*val[i];
	for(int i=1;i<=34;++i)val[i]=0;
	return ans*7;
}
/*--------------胡牌-------------*/
int dp[40][7][7][7][7]; 
//前i张牌,有j个面子,有没有雀头 i还剩几张,i-1还剩几张,
//dp[i][j][k][l][m]
bool ck(int x){
	if(x==28||x==29||x==30||x==31||x==32||x==33||x==34)return false;
	return true;
}
inline int Getv(int pos,int num){
	return vis[pos]?1LL<<num:1;
}
inline bool Ck(int x,int y,int z){
	return ck(y)&&ck(x)&&ck(z)&&(((z<=9)&&((x)>=1))||((z>9)&&(z)<=18&&x>9)||(z>18&&z<=27&&x>18));
}
int general(){
	dp[0][0][0][0][0]=1;
	for(int i=0;i<=33;++i)
		for(int j=0;j<=4;++j)
			for(int k=0;k<=1;++k)
				for(int l=0;l<=num[i];++l)//当前 
					for(int mm=0;mm<=num[i-1];++mm){//上一个 
						int cd=i+1;
						int numc=num[cd];
						int Len1=num[i];
						int Len2=num[i-1];
						if(!dp[i][j][k][l][mm])continue;
						if(numc>=3){//刻子 
							dp[i+1][j+1][k][numc-3][l]=Max(dp[i+1][j+1][k][numc-3][l],dp[i][j][k][l][mm]*Getv(i+1,3)*C(numc,3));
						}
						
						//顺子1 	
						if(l>0&&mm>0&&numc>0&&Ck(i-1,i,i+1)){
							dp[i+1][j+1][k][numc-1][l-1]=Max(dp[i+1][j+1][k][numc-1][l-1],dp[i][j][k][l][mm]*Getv(i-1,1)*Getv(i,1)*Getv(i+1,1)*numc*C(num[i-1],num[i-1]-mm+1)*C(num[i],num[i]-l+1)/C(Len2,Len2-mm)/C(Len1,Len1-l));
						}
						
						//顺子2 
						if(l>1&&mm>1&&numc>1&&Ck(i-1,i,i+1)){
							dp[i+1][j+2][k][numc-2][l-2]=Max(dp[i+1][j+2][k][numc-2][l-2],dp[i][j][k][l][mm]*Getv(i-1,2)*Getv(i,2)*Getv(i+1,2)*C(numc,2)*C(Len2,Len2-mm+2)*C(Len1,Len1-l+2)/C(Len2,Len2-mm)/C(Len1,Len1-l));
						}
						
						//单雀头 
						if(numc>=2&&!k)dp[i+1][j][k^1][numc-2][l]=Max(dp[i+1][j][k^1][numc-2][l],dp[i][j][k][l][mm]*Getv(i+1,2)*C(numc,2));
						
						
						//雀头带单顺子 
						if(!k&&numc>=3&&l>0&&mm>0&&Ck(i-1,i,i+1))
							dp[i+1][j+1][k^1][numc-3][l-1]=Max(dp[i+1][j+1][k^1][numc-3][l-1],dp[i][j][k][l][mm]*Getv(i+1,3)*Getv(i,1)*Getv(i-1,1)*C(Len2,Len2-mm+1)*C(Len1,Len1-l+1)/C(Len2,Len2-mm)/C(Len1,l)*C(numc,3));
						
						
						//雀头带双顺子 
						if(!k&&numc>=4&&l>1&&mm>1&&Ck(i-1,i,i+1))
							dp[i+1][j+2][k^1][numc-4][l-2]=Max(dp[i+1][j+2][k^1][numc-4][l-2],dp[i][j][k][l][mm]*Getv(i+1,4)*Getv(i,2)*Getv(i-1,2)*C(Len2,Len2-mm+2)*C(Len1,Len1-l+2)/C(Len2,Len2-mm)/C(Len1,Len1-l));
						//顺子带刻子
						if(numc>=4&&l>0&&mm>0&&Ck(i-1,i,i+1)){
							dp[i+1][j+2][k][numc-4][l-1]=Max(dp[i+1][j+2][k][numc-4][l-1],dp[i][j][k][l][mm]*Getv(i+1,4)*Getv(i,1)*Getv(i-1,1)*C(Len1,Len1-l+1)*C(Len2,Len2-mm+1)/C(Len1,Len1-l)/C(Len2,Len2-mm));
						} 
						
						
						//不选 
						dp[i+1][j][k][numc][l]=Max(dp[i+1][j][k][numc][l],dp[i][j][k][l][mm]);
					}
//	//----------------------------------------------------------------------------------------------------------------------------------
	int ans=-1;
	for(int i=0;i<=34;++i)
		for(int j=0;j<=4;++j)
			for(int k=0;k<=4;++k)
				ans=Max(ans,dp[i][4][1][j][k]);
	return ans;
	
}
void clear(){
	for(int i=0;i<=35;++i)
		for(int j=0;j<=5;++j)
			for(int k=0;k<=1;++k)
				for(int l=0;l<=5;++l)
					for(int mm=0;mm<=5;++mm)
						dp[i][j][k][l][mm]=0;
}
char getcha(){char x;cin>>x;return x;}
int Readchar(){
	char ch=getcha();
	if(ch=='0')return 0;
	if(isdigit(ch)){
		char c=getcha();
		if(c=='m')return (int)ch-'0';
		if(c=='p')return (int)ch-'0'+9;
		if(c=='s')return (int)ch-'0'+18;
	}
	else{
		switch(ch){
			case 'E':return 28;
			case 'W':return 29;
			case 'S':return 30;
			case 'N':return 31;
			case 'Z':return 32;
			case 'B':return 33;
			case 'F':return 34;
		}
	}
	return 0;
}
void Init(){
	int TT;
	scanf("%lld",&TT);
	while(TT--){
		pre();
		do{
			int x=Readchar();
			if(x==0)break;
			num[x]--;
			if(x>27||x==1||x==9||x==10||x==18||x==19||x==27)card++;
		}while(1);
		do{
			int x=Readchar();
			if(x==0)break;
			vis[x]=1;
		}while(1);
		card=13*4-card;
		int Ans=Max(equal(),Max(solve_seven(),general()));
//		printf("%lld %lld %lld\n",equal(),solve_seven(),general());
		printf("%lld\n",Ans);
		clear();
	}
}
signed main(){
//	freopen("111.txt","r",stdin);
	v.push_back(1);
	v.push_back(9);
	v.push_back(10);
	v.push_back(18);
	v.push_back(19);
	v.push_back(27);
	v.push_back(28);
	v.push_back(29);
	v.push_back(30);
	v.push_back(31);
	v.push_back(32);
	v.push_back(33);
	v.push_back(34);
	Init();
	return 0;
}

标签:cdot,题解,Getv,GZOI2019,GXOI,numc,Num,dp,choose
来源: https://www.cnblogs.com/h-lka/p/14993023.html