习题:宝牌一大堆(DP&卡常)

题目

传送门

思路

这道题主要是状态不好想和题目不好理解

如果你和笔者一样没有接触过麻将

我们首先将整个麻将进行hash处理

定义\(dp_{i,j,k,l,m,n}\)为前i种牌,杠子和面子总共的数量为j,雀子的数量为k,第\(i-2\)种牌的数量为l,第\(i-1\)种牌的数量为m,第种i牌的数量为n

需要特别注意的是,DP的值是不将i-2~i这三种牌考虑进去的

那么可以写出转移

\(\begin{cases}dp_{i,j+1,k,l+1,m+1,n+1}(\mbox{顺子})\\dp_{i,j+1,k,l,m,n+3}(刻子)\\dp_{i,j+1,k,l,m,n+4}(杠头)\\dp_{i,j,k+1,l,m,n+2}(雀子)\end{cases}\)=\(dp_{i,j,k,l,m,n}\)

直接原地转移,因为我们的\(dp\)中不考虑i-2~i种棋牌的​

还有一种情况

\(dp_{i+1,j,k,m,n,0}=dp_{i,j,k,m,n,l}*C_{t_{i}}^l*qkpow(bp_i,l)\)

其中 \(t_i\)表示i种棋牌剩余的个数,\(bp_i=\begin{cases}2[第i种数是宝牌]\\1[第i种不是宝牌]\end{cases}\)

方程的意义就为不考虑i种棋牌时,当前的\(dp\)状态向后转移,也挺好理解的

最后统计一下,统计的时候别忘了加上最后三种棋牌的贡献

接着说一下优化

当前状态为0可以直接跳过

证明:最后的状态一定是一个乘积式,当其中一项为0,整个就直接为0

可以不用考虑杠子

证明:\(C_4^3=4且C_4^4=1\)也就是说即使这张牌是宝牌不是不划算的

但是这并不代表着循环的条件会变,我们只是多continue几个状态

其实都是常数级的减小

但是可以让你从50分直升100分

这道题真是道好(毒瘤)题

常数的重要性一览无余

代码

#pragma GCC optimize(2)
#include<iostream>
#include<cstring>
#include<climits>
#include<queue>
using namespace std;
int T;
priority_queue<int> q;
long long ans;
long long tmp;
int num_pow[3][5];
long long dp[35][5][2][5][5][5];
int sz[35]={0,0,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,0,0,0,0,0,0,0};
int gs[14]={0,1,9,10,18,19,27,28,29,30,31,32,33,34};
int t[35];
int bp[35];
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}
};
string a;
int solve_hash(string a)
{
	if(a[1]=='m')
		return a[0]-'0';
	if(a[1]=='p')
		return a[0]-'0'+9;
	if(a[1]=='s')
		return a[0]-'0'+18;
	if(a[0]=='E')
		return 28;
	if(a[0]=='S')
		return 29;
	if(a[0]=='W')
		return 30;
	if(a[0]=='N')
		return 31;
	if(a[0]=='Z')
		return 32;
	if(a[0]=='B')
		return 33;
	if(a[0]=='F')
		return 34;
}
void init()
{
	while(!q.empty())
		q.pop();
	memset(dp,0,sizeof(dp));
	dp[1][0][0][0][0][0]=1;
	ans=0;
	for(int i=1;i<=34;i++)
	{
		bp[i]=1;
		t[i]=4;
	}
}
int qkpow(int a,int b)
{
	if(num_pow[a][b])
		return num_pow[a][b];
	if(b==0)
		return 1;
	if(b==1)
		return a;
	long long t=qkpow(a,b/2);
	t=(t*t);
	if(b%2==1)
		t*=a;
	num_pow[a][b]=t;
	return t;
}
void c_in()
{
	init();
	while(1)
	{
		cin>>a;
		if(a[0]=='0')
			break;
		t[solve_hash(a)]--;
	}
	while(1)
	{
		cin>>a;
		if(a[0]=='0')
			break;
		bp[solve_hash(a)]=2;
	}
	for(int i=1;i<=13;i++)
	{
		tmp=1;
		for(int j=1;j<=13;j++)
		{
			if(i==j)
			{
				if(t[gs[i]]<2)
					tmp=0;
				else
					tmp=tmp*c[t[gs[i]]][2]*qkpow(bp[gs[i]],2);
			}
			else
			{
				if(!t[gs[j]])
					tmp=0;
				else
					tmp=tmp*c[t[gs[j]]][1]*qkpow(bp[gs[j]],1);
			}
		}
		ans=max(ans,tmp*13);
	}
	for(int i=1;i<=34;i++)
		if(t[i]>=2)
			q.push(c[t[i]][2]*qkpow(bp[i],2));
	if(q.size()>=7)
	{
		tmp=1;
		for(int i=1;i<=7;i++)
		{
			tmp=tmp*q.top();
			q.pop();
		}
		ans=max(ans,tmp*7);
	}
	for(int i=1;i<=34;i++)
	{
		for(int j=0;j<=4;j++)
		{
			for(int k=0;k<=1;k++)
			{
				for(int l=0;l<=4;l++)
				{
					for(int m=0;m<=4;m++)
					{
						for(int n=0;n<=4;n++)
						{
							long long now=dp[i][j][k][l][m][n];
							if(!now)
								continue;	
							if(k==0&&t[i]-n>=2)
								dp[i][j][k+1][l][m][n+2]=max(dp[i][j][k+1][l][m][n+2],now);
							if(t[i]-n>=3&&j<4)
								dp[i][j+1][k][l][m][n+3]=max(dp[i][j+1][k][l][m][n+3],now);
							if(sz[i]&&t[i-2]-l>=1&&t[i-1]-m>=1&&t[i]-n>=1&&j<4)
								dp[i][j+1][k][l+1][m+1][n+1]=max(dp[i][j+1][k][l+1][m+1][n+1],now);
							if(i<34)
								dp[i+1][j][k][m][n][0]=max(dp[i+1][j][k][m][n][0],now*(i>2?c[t[i-2]][l]:1)*qkpow(i>2?bp[i-2]:1,l));
							if(i==34&&j==4&&k==1)
								ans=max(ans,now*c[t[i-2]][l]*qkpow(bp[i-2],l)*c[t[i-1]][m]*qkpow(bp[i-1],m)*c[t[i]][n]*qkpow(bp[i],n));
						}
					}
				}
			}
		}
	}
	cout<<ans<<'\n';
}
int main()
{ 
	ios::sync_with_stdio(false);
	cin>>T;
	for(int i=1;i<=T;i++)
		c_in();
	return 0;
}

没错就是6维

posted @ 2019-12-14 21:41  loney_s  阅读(203)  评论(0)    收藏  举报