Luogu

BZOJ

LOJ

分析

$\mathrm{\color{black}{x}\color{red}{gzc}}$ : 这不是傻逼题吗


首先你要把题目看懂,特别是怎么计算分数的。

我们把三种和牌情况分开处理。

七对子

对每一种牌算一下组成对子的分数,然后贪心地选取前 $7$ 大的即可。

国士无双

国士无双能选的牌叫做幺九牌

枚举哪一种幺九牌有两张再计算即可。

$3\times 4+2$

把刻子、杠子、顺子合称面子

设 $f[i][j][k][l][m][n]$ 表示前 $i$ 种牌,有 $j$ 个面子、 $k$ 个雀头,选了 $l$ 张 $i-2$ 、 $m$ 张 $i-1$ 和 $n$ 张 $i$ 的最大得分。

注意取值范围: $1\leq i\leq 34,0\leq j,l,m,n\leq 4,0\leq k\leq 1$ 。

转移比较简单,分五种情况即可,详见代码。


另外这题细节比较多,详见代码。

代码

// =================================
//   author: M_sea
//   website: http://m-sea-blog.com/
// =================================
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define re register
typedef long long ll;
using namespace std;

template <typename T>
inline void chkmax(T& x,T y) { if (y>x) x=y; }
inline int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

int cnt[40],tr[40],C[10][10];

inline int id(char s[3]) {
    if (s[0]=='E') return 28;
    if (s[0]=='S') return 29;
    if (s[0]=='W') return 30;
    if (s[0]=='N') return 31;
    if (s[0]=='Z') return 32;
    if (s[0]=='B') return 33;
    if (s[0]=='F') return 34;
    if (s[1]=='m') return s[0]-'0';
    if (s[1]=='p') return s[0]-'0'+9;
    if (s[1]=='s') return s[0]-'0'+18;
}

inline int ex(int i,int c) { return tr[i]?(1<<c):1; } //宝牌分数

namespace normal {
    const int can[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};
    ll f[40][5][2][5][5][5];
    
    inline ll work() {
        memset(f,0,sizeof(f)); f[1][0][0][0][0][0]=1;
        ll res=0;
        for (re int i=1;i<=34;++i)
            for (re int j=0;j<=4;++j)
                for (re int k=0;k<=1;++k)
                    for (re int l=0;l<=4;++l)
                        for (re int m=0;m<=4;++m)
                            for (re int n=0;n<=4;++n) {
                                ll now=f[i][j][k][l][m][n];
                                if (!now) continue;
                                //跳过
                                if (i<34)
                                    chkmax(f[i+1][j][k][m][n][0],now*(i>2?C[cnt[i-2]][l]:1)*ex(i-2,l));
                                //刻子
                                if (j<4&&cnt[i]-n>=3)
                                    chkmax(f[i][j+1][k][l][m][n+3],now);
                                //杠子
                                if (j<4&&cnt[i]-n>=4)
                                    chkmax(f[i][j+1][k][l][m][n+4],now);
                                //顺子
                                if (j<4&&can[i]&&cnt[i]-n&&cnt[i-1]-m&&cnt[i-2]-l)
                                    chkmax(f[i][j+1][k][l+1][m+1][n+1],now);
                                //雀头
                                if (k<1&&cnt[i]-n>=2)
                                    chkmax(f[i][j][k+1][l][m][n+2],now);
                                //结算
                                if (i==34&&j==4&&k==1)
                                    chkmax(res,now*C[cnt[i-2]][l]*ex(i-2,l)*C[cnt[i-1]][m]*ex(i-1,m)*C[cnt[i]][n]*ex(i,n));
                            }
        return res;
    }
}

namespace chitoi { //七对子
    ll s[40];

    inline int cmp(ll a,ll b) { return a>b; }
    
    inline ll work() {
        for (re int i=1;i<=34;++i) {
            if (cnt[i]<2) s[i]=0;
            else s[i]=C[cnt[i]][2]*ex(i,2);
        }
        sort(s+1,s+34+1,cmp);
        ll res=1;
        for (re int i=1;i<=7;++i) res*=s[i];
        return res*7;
    }
}

namespace kokushi { //国士无双
    const int yao[13]={1,9,10,18,19,27,28,29,30,31,32,33,34}; //幺九牌
    
    inline ll work() {
        ll res=0;
        for (re int i=0;i<13;++i) {
            if (!cnt[yao[i]]) return 0;
            if (cnt[yao[i]]<2) continue;
            ll now=C[cnt[yao[i]]][2]*ex(yao[i],2);
            for (re int j=0;j<13;++j) {
                if (i==j) continue;
                now=now*C[cnt[yao[j]]][1]*ex(yao[j],1);
            }
            chkmax(res,now);
        }
        return res*13;
    }
}

int main() {
    C[0][0]=1;
    for (re int i=1;i<=5;++i) {
        C[i][0]=1;
        for (re int j=1;j<=i;++j) C[i][j]=C[i-1][j]+C[i-1][j-1];
    }
    int T=read();
    while (T--) {
        for (re int i=1;i<=34;++i) cnt[i]=4,tr[i]=0;
        char tmp[3];
        while (scanf("%s",tmp)) {
            if (tmp[0]=='0') break;
            --cnt[id(tmp)];
        }
        while (scanf("%s",tmp)) {
            if (tmp[0]=='0') break;
            tr[id(tmp)]=1;
        }
        ll ans=0;
        chkmax(ans,normal::work());
        chkmax(ans,chitoi::work());
        chkmax(ans,kokushi::work());
        printf("%lld\n",ans);
    }
    return 0;
}
最后修改:2019 年 09 月 26 日 01 : 19 PM