P5301 [GXOI/GZOI2019]宝牌一大堆

题目

P5301 [GXOI/GZOI2019]宝牌一大堆

不简化了,太长了

思路

做的第一道NOI/NOI+/CTSC\(DP\),做这个题要时刻记得各种牌是怎么组成的,\(qwq\)

雀魂大毒瘤,麻将已无爱

  • 七对子

直接贪心选择权值最高的七个即可

  • 国士无双

暴力枚举,\(O(13^2)\)

  • \(3*4+2\)

\(f[i][j][k][a][b][c]\)表示枚举到第\(i\)张牌,已经有了\(j\)张面子,有有\(k\)对雀头也可,第\(i\)种牌用了\(a\)张,第\(i+1\)种用了\(b\)张,第\(i+3\)种用了\(c\)张的最大分数,或者\(k=0/1\)

然后枚举转移即可

  • 性质\(1\)

经过艰难的读题后可以发现杠子是不需要考虑的,因为杠子的价值\(C_4^{4}=1\)就算是宝牌也没有刻子的高\(C_4^{3}=4\)

  • 性质\(2\)

三个刻顺子是没有三个刻子更优的,所有对于\(l,m\)只需要枚举到\(2\)就行了

  • 性质\(3\)

\(f\)值是\(0\)的话说明不合法直接退出即可

  • 转移方程

\(f[i][j][1][k+2][l][m]=max(f[i][j][1][k+2][l][m],\frac{f[i][j][0][k][l][m]}{C_{a[i]}^k}*C_{a[i]}^{k+2}*dora[i]^2)\)

\(f[i][j+1][o][k+3][l][m]=max(f[i][j+1][o][k+3][l][m],\frac{f[i][j][o][k][l][m]}{C_{a[i]}^k}*C_{a[i]}^{k+3}*dora[i]^3)\)

\(f[i][j+1][o][k+1][l+1][m+1]=\max(f[i][j+1][o][k+1][l+1][m+1],\frac{f[i][j][o][k][l][m]}{c_{a[i]}^k}*c_{a[i]}^{k+1}*\frac{dora[i]}{c_{a[i+1]}^l}*c_{a[i+1]}^{l+1}*\frac{dora[i+1]}{c_{a[i+2]}^m}*c_{a[i+2]}^{m+1}*dora[i+2])\)

  • 注意

注意在转移之前判断合法不合法(剩的牌够不够),开long long
为什么要开\(O2\)才能过?我吧我忘记判断不合法状态了,\(qwq\)

复杂度为\(O(34*4*4*2*2)=O(2176)\),期望得分:\(100\)

code

/*
@ author:pyyyyyy/guhl37
-----思路------

-----debug-------

*/
#include<queue>
#include<algorithm>
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=55;
char si[5];
int a[N],dora[N],ans,dg[N];
int f[N][5][2][5][3][3];
int c[5][5]= {
	1,0,0,0,0,
	1,1,0,0,0,
	1,2,1,0,0,
	1,3,3,1,0,
	1,4,6,4,1
};
int yaoku[15]= {0,1,9,10,18,19,27,28,29,30,31,32,33,34};
bool isyaoku[35]= {0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1};
bool tail[35]= {0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1};
int T;
void read() {
	while(1) {
		/*统计没出手的牌数*/
		scanf("%s",si);
		if(si[0]=='0') break;
		else if(si[1]=='p') a[si[0]-'0']--;
		else if(si[1]=='s') a[si[0]-'0'+9]--;
		else if(si[1]=='m') a[si[0]-'0'+18]--;
		else if(si[0]=='E') a[28]--;
		else if(si[0]=='S') a[29]--;
		else if(si[0]=='W') a[30]--;
		else if(si[0]=='N') a[31]--;
		else if(si[0]=='B') a[32]--;
		else if(si[0]=='F') a[33]--;
		else if(si[0]=='Z') a[34]--;
	}
	while(1) {
		/*统计宝牌*/
		scanf("%s",si);
		if(si[0]=='0') break;
		else if(si[1]=='p') dora[si[0]-'0']=2;
		else if(si[1]=='s') dora[si[0]-'0'+9]=2;
		else if(si[1]=='m') dora[si[0]-'0'+18]=2;
		else if(si[0]=='E') dora[28]=2;
		else if(si[0]=='S') dora[29]=2;
		else if(si[0]=='W') dora[30]=2;
		else if(si[0]=='N') dora[31]=2;
		else if(si[0]=='B') dora[32]=2;
		else if(si[0]=='F') dora[33]=2;
		else if(si[0]=='Z') dora[34]=2;
	}
}
void solve1() {
	for(int i=1; i<=13; ++i) {
		int x=yaoku[i],tmp=13;
		tmp*=c[a[x]][2]*dora[x]*dora[x];
		for(int j=1; j<=13; ++j) {
			if(i==j) continue;
			x=yaoku[j];
			tmp*=a[x]*dora[x];
		}
		ans=max(ans,tmp);
	}
}
void solve2() {
	priority_queue<int> q;
	int tmp=7;
	for(int i=1; i<=34; ++i) q.push(c[a[i]][2]*dora[i]*dora[i]);
	int now;
	for(int i=1; i<=7; ++i) {
		now=q.top();
		q.pop();
		tmp*=now;
	}
	ans=max(ans,tmp);
	while(!q.empty()) q.pop();
}
void solve3() {
	for(int i=1; i<=34; ++i) {
		for(int j=0; j<=4; ++j) {
			for(int k=0; k<=4; ++k) {
				for(int l=0; l<=2; ++l) {
					for(int m=0; m<=2; ++m) {
						if(!f[i][j][0][k][l][m]&&!f[i][j][1][k][l][m]) continue;
						if(a[i]-k>=2) f[i][j][1][k+2][l][m]=max(f[i][j][1][k+2][l][m],f[i][j][0][k][l][m]/c[a[i]][k]*c[a[i]][k+2]*dora[i]*dora[i]);
						if(j<4) {
							if(a[i]-k>=3)
								for(int o=0; o<=1; ++o)
									f[i][j+1][o][k+3][l][m]=max(f[i][j+1][o][k+3][l][m],f[i][j][o][k][l][m]/c[a[i]][k]*c[a[i]][k+3]*dora[i]*dora[i]*dora[i]);
							if((!tail[i])&&a[i]-k>0&&a[i+1]-l>0&&a[i+2]-m>0&&l!=2&&m!=2)
								for(int o=0; o<=1; ++o) f[i][j+1][o][k+1][l+1][m+1]=max(f[i][j+1][o][k+1][l+1][m+1],f[i][j][o][k][l][m]/c[a[i]][k]*c[a[i]][k+1]*dora[i]/c[a[i+1]][l]*c[a[i+1]][l+1]*dora[i+1]/c[a[i+2]][m]*c[a[i+2]][m+1]*dora[i+2]);
						}
						f[i+1][j][0][l][m][0]=max(f[i+1][j][0][l][m][0],f[i][j][0][k][l][m]);
						f[i+1][j][1][l][m][0]=max(f[i+1][j][1][l][m][0],f[i][j][1][k][l][m]);
						if(j==4) dg[i]=max(dg[i],f[i][j][1][k][l][m]);
					}
				}
			}
		}
	}
	for(int i=1; i<=35; ++i) ans=max(ans,dg[i]);
}

signed main() {
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	scanf("%lld",&T);
	while(T--) {
		memset(dg,0,sizeof(dg));
		memset(f,0,sizeof(f));
		f[1][0][0][0][0][0]=1;
		for(int i=1; i<=34; ++i) a[i]=4,dora[i]=1;
		ans=0;
		read();
		solve1();//国士无双
		solve2();//七对子
		solve3();//3*4+2
		cout<<ans<<'\n';
	}
	return 0;
}
posted @ 2020-06-17 17:39  pyyyyyy  阅读(263)  评论(0编辑  收藏  举报