LOJ#3084. 「GXOI / GZOI2019」宝牌一大堆(递推)

题面

传送门

题解

为什么又是麻将啊啊啊!而且还是我最讨厌的爆搜类\(dp\)……

首先国士无双和七对子是可以直接搞掉的,关键是剩下的,可以看成\(1\)个雀头加\(4\)个杠子或面子

直接\(dp\),设\(f[i][j][k][l][x][y]\)表示考虑前\(i\)种牌,以第\(i-2\)种牌为开头的顺子张数为\(j\),以\(i-1\)为开头的顺子张数为\(k\),以\(i\)开头的顺子张数为\(l\),杠子加面子总数为\(x\),雀头个数为\(y\),的最大权值

注意一些边界条件,比方说以某一种牌开头的顺子选的不需要超过\(2\)个,因为如果选了\(3\)个完全可以拆成刻子。还有有的时候以某种牌为开头的顺子可能不合法。以及牌数加起来不能超

这里其实可以把\(l\)这一维用滚动数组滚掉

代码基本都是抄\(fcw\)

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define inline __inline__ __attribute__((always_inline))
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
const int N=55;
inline ll max(R ll x,R ll y){return x>y?x:y;}
ll bin[N],C[N][N];int a[N],b[N];
inline ll ch(R int x,R int v){return C[a[x]][v]*bin[b[x]*v];}
int id(char *w){
	if(strlen(w+1)==2){
		switch(w[2]){
			case 'm':return w[1]-'0';
			case 'p':return 9+w[1]-'0';
			case 's':return 18+w[1]-'0';
		}
	}else{
		switch(w[1]){
			case 'E':return 28;
			case 'W':return 29;
			case 'N':return 30;
			case 'S':return 31;
			case 'Z':return 32;
			case 'B':return 33;
			case 'F':return 34;
		}
	}
	return -1;
}
ll c[N];
ll solve1(){
	ll res=1;
	fp(i,1,34)c[i]=ch(i,2);
	sort(c+1,c+1+34);
	fp(i,28,34)res*=c[i];return res*7;
}
int d[20]={0,1,9,10,18,19,27,28,29,30,31,32,33,34};
ll solve2(){
	ll res=0,tmp;
	fp(i,1,13){
		tmp=ch(d[i],2);
		fp(j,1,13)if(i!=j)tmp*=ch(d[j],1);
		cmax(res,tmp);
	}return res*13;
}
ll f[35][3][3][5][2];
ll solve3(){
	memset(f,0,sizeof(f)),f[0][0][0][0][0]=1;
	fp(i,0,34){
		fp(j,0,2)if(!j||(i<=27&&i%9!=0&&i%9!=1)){
			fp(k,0,2)if(!k||(i<=27&&i%9!=8&&i%9!=0))
				if(a[i+1]>=j+k){
					fp(x,j+k,4)fp(y,0,1)if(f[i][j][k][x][y]){
							for(R int z=0;z<=2&&j+k+z<=a[i+1]&&x+z<=4;++z)
								for(R int w=0;j+k+z+w*3<=a[i+1]&&x+z+w<=4;++w){
									int t=j+k+z+w*3;
									cmax(f[i+1][k][z][x+z+w][y],f[i][j][k][x][y]*ch(i+1,t));
									if(!y&&t+2<=a[i+1])cmax(f[i+1][k][z][x+z+w][1],f[i][j][k][x][y]*ch(i+1,t+2));
								}
							if(a[i+1]-j-k==4&&x<4)cmax(f[i+1][k][0][x+1][y],f[i][j][k][x][y]*ch(i+1,4));
						}
				}
		}
	}
	return f[34][0][0][4][1];
}
void init(){
	bin[0]=1;fp(i,1,18)bin[i]=bin[i-1]<<1;
	fp(i,0,4){
		C[i][0]=1;
		fp(j,1,i)C[i][j]=C[i-1][j-1]+C[i-1][j];
	}
}
char w[15];
void solve(){
	fp(i,1,34)a[i]=4,b[i]=0;
	while(true){
		scanf("%s",w+1);
		if(w[1]=='0')break;
		--a[id(w)];
	}
	while(true){
		scanf("%s",w+1);
		if(w[1]=='0')break;
		b[id(w)]=1;
	}
	printf("%lld\n",max(solve1(),max(solve2(),solve3())));
}
int main(){
//	freopen("testdata.in","r",stdin);
	int T;scanf("%d",&T);init();
	while(T--)solve();
	return 0;
}
posted @ 2019-04-16 19:58  bztMinamoto  阅读(264)  评论(0编辑  收藏  举报
Live2D