「GXOI / GZOI2019」宝牌一大堆 (DP)

题意

LOJ传送门

题解

可以发现「七对子」 和 「国士无双」直接暴力就行了。

唯一的就是剩下的"3*4+2"。

考试的时候写了个爆搜剪枝,开了O2有50pts。写的时候发现可以DP,但是没写。

然后下来写了发现就4个转移。。。

用$dp[i][j][k][a][b][c]$表示当考虑前$i$张牌,有$j$个雀头,$k$个面子,$i-2$用了$a$张,$i-1$用了$b$张,$i$用了$c$张时,前$i-3$张牌的最大分数。

注意这里是"前$i-3$张牌的最大分数",也就是没有计算$a,b,c$这最后$3$种牌的贡献。

有个性质就是杠子一定不会选,因为选杠子一定劣于面子。
因为$C(4,4)=1<C(4,3)=4$,就算这张牌是宝牌,杠子的贡献是$C(4,4)2^4<C(4,3)2^3$

所以我们没有考虑杠子。

上代码

CODE

luogu上开了O2才过

#include <bits/stdc++.h>
using namespace std;
inline int getid(char *s) {
    if(isdigit(s[0])) {
        int re = s[0]-'0';
        if(s[1] == 'm') return re;
        if(s[1] == 'p') return 9+re;
        if(s[1] == 's') return 18+re;
    }
    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;
}
typedef long long LL;
int cnt[50], bel[50];
LL ans, c[5][5], pw6[10];
bool bao[50];

int arr13[14] = { 0, 1, 9, 10, 18, 19, 27, 28, 29, 30, 31, 32, 33, 34 }; //指定的13张牌的编号
void solve13() {
    bool flg = 1;
    for(int i = 1; i <= 13; ++i) flg &= bool(cnt[arr13[i]]);
    if(flg) {
        LL now = 13;
        for(int i = 1; i <= 13; ++i)
            now = now * c[cnt[arr13[i]]][1] * (bao[arr13[i]] ? 2 : 1);
        for(int i = 1; i <= 13; ++i)
            ans = max(ans, now / c[cnt[arr13[i]]][1] * c[cnt[arr13[i]]][2] * (bao[arr13[i]] ? 2 : 1));
    }
}

void dfs(int i, int j, LL now) { //暴力枚举七对子
    if(j == 7) { ans = max(now, ans); return; }
    if(i > 34) return;
    LL tmp = now*pw6[7-j]*(1ll<<((7-j)<<1)); //剪枝,预估之后的最大值,如果小于等于当前答案就不用了搜下去了
    if(tmp <= ans) return;
    if(cnt[i] >= 2) {
        cnt[i] -= 2;
        dfs(i+1, j+1, now*c[cnt[i]+2][2]*(bao[i]?4:1));
        cnt[i] += 2;
    }
    if(tmp <= ans) return;
    dfs(i+1, j, now);
}
void solve7() { dfs(1, 0, 7); }

LL dp[36][2][5][5][5][5];
inline void upd(LL &x, LL y) { x = y > x ? y : x; }
inline int calc(int i, int j) { return i > 0 ? c[cnt[i]][j] * (bao[i] ? (1<<j) : 1) : 1; } //计算i选j张的贡献
void solve() {
    memset(dp, 0, sizeof dp);
    dp[1][0][0][0][0][0] = 1;
    for(int i = 1; i <= 34; ++i)
        for(int j = 0; j < 2; ++j)
            for(int k = 0; k <= 4; ++k)
                for(int a = 0; a <= 4; ++a)
                    for(int b = 0; b <= 4; ++b)
                        for(int c = 0; c <= 4; ++c) {
                            LL val = dp[i][j][k][a][b][c];
                            if(val) {
                                upd(dp[i+1][j][k][b][c][0], val * calc(i-2, a)); 
                                if(!j && cnt[i]-c >= 2)
                                    upd(dp[i][j+1][k][a][b][c+2], val);
                                if(k<4 && cnt[i]-c >= 3)
                                    upd(dp[i][j][k+1][a][b][c+3], val);
                                if(k<4 && bel[i] && i>2 && bel[i]==bel[i-2] && cnt[i]-c >= 1 && cnt[i-1]-b >= 1 && cnt[i-2]-a >= 1)
                                    upd(dp[i][j][k+1][a+1][b+1][c+1], val);
                            }
                    }
    for(int a = 0; a <= 4; ++a)
        for(int b = 0; b <= 4; ++b)
            for(int c = 0; c <= 4; ++c) //最后答案算上a,b,c
                upd(ans, dp[34][1][4][a][b][c] * calc(32, a) * calc(33, b) * calc(34, c)); 
}

int main () {
    //freopen("doraippai.in", "r", stdin);
//  freopen("doraippai.out", "w", stdout);
    for(int i = 1; i <= 9; ++i) bel[i] = 1;
    for(int i = 10; i <= 18; ++i) bel[i] = 2;
    for(int i = 19; i <= 27; ++i) bel[i] = 3;
    c[0][0] = 1;
    for(int i = 1; i <= 4; ++i) {
        c[i][0] = c[i][i] = 1;
        for(int j = 1; j < i; ++j)
            c[i][j] = c[i-1][j-1] + c[i-1][j];
    }
    pw6[0] = 1;
    for(int i = 1; i <= 7; ++i) pw6[i] = 6ll * pw6[i-1];
    int T; scanf("%d", &T); while(T--) {
        for(int i = 1; i <= 34; ++i)
            cnt[i] = 4, bao[i] = 0;
        char s[5];
        while(scanf("%s", s), s[0] != '0') {
            int num = getid(s);
            --cnt[num];
        }
        while(scanf("%s", s), s[0] != '0') {
            int num = getid(s);
            bao[num] = 1;
        }
        ans = 0;
        solve13();
        solve7();
        solve();
        printf("%lld\n", ans);
    }
}

猜你喜欢

转载自www.cnblogs.com/Orz-IE/p/12051413.html