习题:宝牌一大堆(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维

原文地址:https://www.cnblogs.com/loney-s/p/12041057.html

时间: 2024-10-06 14:13:36

习题:宝牌一大堆(DP&卡常)的相关文章

[GXOI/GZOI2019]宝牌一大堆(dp)

luogu     bzoj 这个麻将题还算挺友善的,比隔壁zjoi的要好得多... 比较正常的做法是五维dp 但事实上六维dp也是完全不会被卡的 七对子选权值最高的七个,国士无双直接$13^2$暴力 $dp[i][j][0/1][k][l][m]$表示枚举到了第i张牌,已经凑了j个面子,有无雀头,第i张牌已经用了k张,第i+1张牌用了l张,第i+2张牌用了m张,直接暴力转移... 然后你会得到50... 当然需要优化. 优化1: 枚举到dp值为0的直接continue,这样的不合法牌型有很多可

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

这道题除了非常恶心以外也没有什么非常让人恶心的地方 当然一定要说有的话还是有的,就是这题和咱 ZJOI 的 mahjong 真的是好像的说~ 于是就想说这道题出题人应该被 锕 掉 noteskey 整体的思路就是特判国士无双和七对子,然后 dp 搞普通的胡牌 dp 状态设计和楼上大佬说的一样,就是用一个五维的 \(f[i][j][k][l][p]\) 表示当前处理了前 i 种类型的牌,存在 j 个 面子/杠子 ,以 i-1 开头的顺子要选 k 个,以 i 开头的面子要选 l 个,以及当前是否有

CodeForces 327E Axis Walking(状压DP+卡常技巧)

Iahub wants to meet his girlfriend Iahubina. They both live in Ox axis (the horizontal axis). Iahub lives at point 0 and Iahubina at point d. Iahub has n positive integers a1, a2, ..., an. The sum of those numbers is d. Suppose p1, p2, ..., pn is a p

[JLOI2013]卡牌游戏 概率DP

[JLOI2013]卡牌游戏 概率DP 题面 \(dfs\)复杂度爆炸,考虑DP.发现决策时,我们只用关心当前玩家是从庄家数第几个玩家与当前抽到的牌是啥.于是设计状态\(f[i][j]\)表示有\(i\)个人时,从庄家数第\(j\)个人的胜率.又因为此时终态确定\(f[1][1]=1\)(只有一个人时那个人胜率为100%),所以倒推回去. 转移时,枚举抽到的牌,算出从庄家数第\(t\)个会出局,那么下一局庄家就是第\(t+1\)个,当前局第\(j\)个就是下一局的第\(j-t(t< j)\)或\

[CSP-S模拟测试]:卡常题/b(基环树+DP)

题目描述 $ρ$有一个二分连通无向图,$X$方点.$Y$方点均为$n$个(编号为$1\sim n$).这个二分图比较特殊,每一个$Y$方点的度为$2$,一条黑色边,一条白色边.所有黑色边权值均为$a$,所有白色边权值均为$b$.选择一个$X$方点,代价为连接的所有边的权值之和.激活一个$Y$方点,需要选择至少一个与之相邻的$X$方点.现在,$ρ$想激活每个$Y$方点,他想知道最小的总代价.不过$ρ$很善良,他给你开了$O2$优化.这样你就不会被卡常了.当然,除非你真的连读入优化都不想写,或者常数

cocos2d-x 3.3 之卡牌设计 NO.3 卡牌移动

上次说了如何播放卡牌翻转的动画,卡牌翻到正面后,就需要让玩家将卡牌拖拽至出场区域或者墓地区域了. 这里重复一下之前的内容: 1.重载触控函数: virtual bool onTouchBegan(Touch* touch, Event* event); virtual void onTouchMoved(Touch* touch, Event* event); virtual void onTouchEnded(Touch* touch, Event* event); 2.在init里添加: /

网络麻将的宝牌设计

今天在帮客户改东北乾安的麻将,乾安麻将的宝牌比效复杂,需求如下:6.1 宝牌:可以替代任何牌的"万能牌".6.2 宝牌产生:牌墙中的最后一张牌为"宝"牌.6.3 宝牌不能查看.6.4 换宝:宝牌被宝杠以后,牌墙中倒数第2张牌为宝牌.6.5 用户听牌后才能使用宝牌. 开始搞了,首选给宝牌定一个变量:看了一下代码,直接把财神当宝牌用了. 首先在开始游戏的地方把宝牌重置. 在用户听牌的动作上加一个读取宝牌的代码,从库存牌变量里取最后一张,这里注意,库存牌里最后一张的key

Codeforces 988D Points and Powers of Two 【性质】【卡常】

这道题关键在于想到两个性质,想到就好做了.这还是我做过的第一道卡常题 1.满足题目中条件的子集,其中元素个数不能大于3 2.如果最大子集为3的话,那一定是x-2^i,  k, x+2^i的形式,我们枚举x就好了,然后i的次数是log10^9:如果最大子集是2,那就是x,x+2^i的形式,同样枚举x:如果最大子集是1,输出a[1]就行 整体复杂度是O(n*logn*log10^9) 1 #include<iostream> 2 #include<set> 3 using namesp

bzoj4028 [HEOI2015]公约数数列(分块+卡常?)

被卡常卡到怀疑人生. 思维又难又卡常(可能是我写的太丑了)心态炸了. 最后还是照题解打的.(题解多了一个排序,似乎快了很多) 所以代码就不发了... 原文地址:https://www.cnblogs.com/Xu-daxia/p/9465265.html