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

原题传送门

首先先要学会麻将,然后会发现就是一个暴力dp,分三种情况考虑:

1.非七对子国士无双,设\(dp_{i,j,k,a,b}\)表示看到了第\(i\)种牌,一共有\(j\)\(i-1\)开头的顺子,有\(k\)\(i\)开头的顺子,有\(a\)个面子/杠子,有\(b\)个雀头时最大分数,暴力转移即可

2.七对子,设\(dp_{i,j}\)表示看到了第\(i\)种牌,一共有\(j\)个雀头时最大分数,暴力转移即可

3.国士无双,设\(dp_{i,j}\)表示看到了国士无双限定的第\(i\)张牌,已经选了\(j\)张牌时最大分数,暴力转移即可

最后比个最大就星了

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline void write(register ll x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
int T,v,cnt[35],mrk[35];
ll 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}
};
ll bin[5]={1,2,4,8,16};
inline void upd(register ll &x,register ll y)
{
    x=x>y?x:y;
}
inline ll chose(register int x,register int y)
{
    return C[cnt[x]][y]*(mrk[x]?bin[y]:1);
}
inline ll work1()
{
    static ll dp[35][3][3][5][2];
    memset(dp,0,sizeof(dp));
    dp[0][0][0][0][0]=1;
    for(register int i=0;i<34;++i)
        for(register int j=0;j<3;++j)
            if(!j||i>7&&(i-7)%9!=0&&(i-7)%9!=1)
                for(register int k=0;k<3;++k)
                    if(!k||i>7&&(i-7)%9!=8&&(i-7)%9!=0)
                        if(cnt[i+1]>=j+k)
                            for(register int a=j+k;a<=4;++a)
                                for(register int b=0;b<=1;++b)
                                    if(dp[i][j][k][a][b])
                                    {
                                        for(register int c=0;c<=2&&j+k+c<=cnt[i+1]&&a+c<=4;++c)
                                            for(register int d=0;j+k+c+d*3<=cnt[i+1]&&a+c+d<=4;++d)
                                            {
                                                int t=j+k+c+d*3;
                                                upd(dp[i+1][k][c][a+c+d][b],dp[i][j][k][a][b]*chose(i+1,t));
                                                if(!b&&t+2<=cnt[i+1])
                                                    upd(dp[i+1][k][c][a+c+d][1],dp[i][j][k][a][b]*chose(i+1,t+2));
                                            }
                                        if(cnt[i+1]-j-k==4&&a<4)
                                            upd(dp[i+1][k][0][a+1][b],dp[i][j][k][a][b]*chose(i+1,4));
                                    }
    return dp[34][0][0][4][1];
}
inline ll work2()
{
    static ll dp[35][8];
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(register int i=0;i<34;++i)
        for(register int j=0;j<=7;++j)
        {
            upd(dp[i+1][j],dp[i][j]);
            if(j<7)
                upd(dp[i+1][j+1],dp[i][j]*chose(i+1,2));
        }
    return dp[34][7]*7;
}
int id[]={0,1,2,3,4,5,6,7,8,16,17,25,26,34};
inline ll work3()
{
    static ll dp[14][15];
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(register int i=0;i<13;++i)
        for(register int j=0;j<=14;++j)
            for(register int k=1;k<=cnt[id[i+1]]&&k<=2;++k)
                upd(dp[i+1][j+k],dp[i][j]*chose(id[i+1],k));
    return dp[13][14]*13;
}
inline int read()
{
    int v;
    char str[5];
    scanf("%s",str);
    if(str[0]=='0')
        return 0;
    if(strlen(str)==1)
    {
        if(str[0]=='E')
            v=1;
        else if(str[0]=='S')
            v=2;
        else if(str[0]=='W')
            v=3;
        else if(str[0]=='N')
            v=4;
        else if(str[0]=='Z')
            v=5;
        else if(str[0]=='B')
            v=6;
        else
            v=7;
    }
    else
    {
        if(str[1]=='m')
            v=7;
        else if(str[1]=='p')
            v=16;
        else
            v=25;
        v+=str[0]-'0';
    }
    return v;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        for(register int i=1;i<=34;++i)
            cnt[i]=4,mrk[i]=0;
        while(v=read())
            --cnt[v];
        while(v=read())
            mrk[v]=1;
        write(max(work1(),max(work2(),work3()))),puts("");
    }
	return 0;
}
posted @ 2019-08-06 08:21  JSOI爆零珂学家yzhang  阅读(301)  评论(1编辑  收藏  举报