题面见:https://www.luogu.com.cn/problem/P5301
题解
枚举+DP
直接枚举七对子和国士无双的情况
3*4+2的情况就DP来做
因为杠子一定不会比顺子优(读者自证不难)
于是我们可以设f[i][j][k][l][m][n]表示
枚举到了第i种牌组成了j个面子、k个雀头,第i、i+1、i+2张牌分别用了l、m、n张所获得的最大分数
于是就可以转移了(刷表法):(其中cb(i,j)为第i种牌选j张的宝牌加成,所有的"="都更新取max)
(雀头)
(刻子)
(顺子)
(推到下一个状态)
转移的时候只刷有值的状态,m,n只用枚举到2,因为三个相同的顺子可以换成三个刻子,这样就可以过了
遇到这种麻将扑克题,不要直接想爆搜,仔细的分析一下状态,就可以用DP来做
像这种大码量DP题一定要仔细,把每一个细节想清楚在写,不要随随便便就开始写,也不要因为畏难而不敢写
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
LL f[36][5][2][5][5][5],g[36],ans;
int cnt[40],C[5][5],pw[6];
bool vis[40],pd[40]={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,0,0};
char ch[4];
int num()
{
if(ch[1]=='m')return ch[0]-48;if(ch[1]=='p')return ch[0]-48+9;if(ch[1]=='s')return ch[0]-48+18;
if(ch[0]=='E')return 28;if(ch[0]=='S')return 29;if(ch[0]=='W')return 30;if(ch[0]=='N') return 31;
if(ch[0]=='Z')return 32;if(ch[0]=='B')return 33;if(ch[0]=='F')return 34;
}
int cb(int x,int ct)
{
if(!vis[x])return 1;
else return pw[ct];
}
LL solve13()
{
int a[14]={0,1,9,10,18,19,27,28,29,30,31,32,33,34};LL sum=1,ret=0;
for(int i=1;i<=13;i++)sum=sum*C[cnt[a[i]]][1]*cb(a[i],1);
if(!sum) return 0;
for(int i=1;i<=13;i++)ret=max(ret,13ll*sum/C[cnt[a[i]]][1]*C[cnt[a[i]]][2]*cb(a[i],1));
return ret;
}
LL solve7()
{
int a[35];LL sum=1;
for(int i=1;i<=34;i++)a[i]=C[cnt[i]][2]*cb(i,2);
sort(a+1,a+35);
for(int i=34;i>=28;i--)sum=1ll*sum*a[i];
return 7ll*sum;
}
LL solve342()
{
int i,j,k,l,m,n,ll,ml,nl;LL tmp,ret=0;
memset(f,0,sizeof(f));memset(g,0,sizeof(g));
f[1][0][0][0][0][0]=1;
for(i=1;i<=34;i++){
for(j=0;j<=4;j++){
for(k=0;k<=1;k++){
for(l=0,ll=cnt[i];l<=4;l++){
for(m=0,ml=cnt[i+1];m<=2;m++){
for(n=0,nl=cnt[i+2];n<=2;n++){
tmp=f[i][j][k][l][m][n];
if(!tmp) continue;
if(l+2<=ll&&k<1)
f[i][j][k+1][l+2][m][n]=max(f[i][j][k+1][l+2][m][n],tmp/C[ll][l]*C[ll][l+2]*cb(i,2));
if(l+3<=ll&&j<4)
f[i][j+1][k][l+3][m][n]=max(f[i][j+1][k][l+3][m][n],tmp/C[ll][l]*C[ll][l+3]*cb(i,3));
if(l<ll&&m<ml&&n<nl&&pd[i] && j<4&&m<2&&n<2)
f[i][j+1][k][l+1][m+1][n+1]=max(f[i][j+1][k][l+1][m+1][n+1],tmp
/C[ll][l]*C[ll][l+1]*cb(i,1)
/C[ml][m]*C[ml][m+1]*cb(i+1,1)
/C[nl][n]*C[nl][n+1]*cb(i+2,1));
f[i+1][j][k][m][n][0]=max(f[i+1][j][k][m][n][0],tmp);
if(j==4&&k==1)g[i]=max(g[i],f[i][j][k][l][n][m]);
}
}
}
}
}
ret=max(ret,g[i]);
}
return ret;
}
int main()
{
int T,i,j;scanf("%d",&T);pw[0]=1;
for(i=0;i<=4;i++){
C[i][0]=C[i][i]=1;pw[i+1]=2*pw[i];
for(j=1;j<i;j++)C[i][j]=C[i-1][j]+C[i-1][j-1];
}
while(T--){
ans=0;for(i=1;i<=34;i++)cnt[i]=4;
memset(vis,0,sizeof(vis));
while(1){scanf("%s",ch);if(ch[0]=='0') break;cnt[num()]--;}
while(1){scanf("%s",ch);if(ch[0]=='0') break;vis[num()]=1;}
ans=max(ans,solve342());
ans=max(ans,solve13());
ans=max(ans,solve7());
printf("%lld\n",ans);
}
}
判断是否可以构成顺子的时候把vis当成pd来用了,结果调到了凌晨1点,后来中午到机房一看,5min就看出来错误了
建议以后都不要深夜写代码调题,既爆肝又调不出来。。。。