[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,这样的不合法牌型有很多可以直接跳过。

优化2:

l和m只枚举到2,原因?如果枚举到三个顺子的话那么我们完全可以用三个刻子等效替代。

优化3:

不需要考虑杠。

原因?

$C_{4}^{3}=4$,$C_{4}^{4}=1$

就算这张牌是宝牌选刻子也必然优于杠子

代码就领略一下精神吧(

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<queue>
  4 #define d01(x) for(int x=0;x<2;x++)
  5 using std::priority_queue;
  6 using std::max;
  7 typedef long long lint;
  8 char si[3];
  9 void dm(lint &kk,lint l){kk=max(kk,l);}
 10
 11 int a[35],dora[35];//1~9,10~18,19~27,28,29,30,31,32,33,34
 12 lint dp[36][5][2][5][3][3];
 13 lint dg[36];
 14 lint ans;
 15 int c[5][5]={
 16     1,0,0,0,0,
 17     1,1,0,0,0,
 18     1,2,1,0,0,
 19     1,3,3,1,0,
 20     1,4,6,4,1
 21 };
 22 int yaoku[15]={0,1,9,10,18,19,27,28,29,30,31,32,33,34};
 23 bool isyaoku[35]={0,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1};
 24 bool tail[35]={0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1};
 25 void kokushi()
 26 {
 27     int x;
 28     for(int i=1;i<=13;i++)
 29     {
 30         x=yaoku[i];
 31         lint tmp=13;
 32         tmp*=c[a[x]][2]*dora[x]*dora[x];
 33         for(int j=1;j<=13;j++)
 34         {
 35             if(i==j) continue;x=yaoku[j];
 36             tmp*=a[x]*dora[x];
 37         }
 38         ans=max(ans,tmp);
 39     }
 40 }
 41 priority_queue<int> q;
 42 void chitoi()
 43 {
 44     lint tmp=7;
 45     for(int i=1;i<=34;i++) q.push(c[a[i]][2]*dora[i]*dora[i]);
 46     int g;
 47     for(int i=1;i<=7;i++)
 48     {
 49         g=q.top();
 50         q.pop();
 51         tmp*=g;
 52     }
 53     ans=max(ans,tmp);
 54     while(!q.empty()) q.pop();
 55 }
 56
 57
 58 void clr()
 59 {
 60     for(int i=1;i<=35;i++)
 61     {
 62         dg[i]=0;
 63         for(int j=0;j<=4;j++)
 64         {
 65             for(int k=0;k<=4;k++)
 66             {
 67                 for(int l=0;l<=2;l++)
 68                 {
 69                     for(int m=0;m<=2;m++)
 70                     dp[i][j][0][k][l][m]=dp[i][j][1][k][l][m]=0;
 71                 }
 72             }
 73         }
 74     }
 75     for(int i=1;i<=34;i++) a[i]=4,dora[i]=1;
 76     ans=0;
 77     dp[1][0][0][0][0][0]=1;
 78 }
 79 int main()
 80 {
 81     int T;
 82     scanf("%d",&T);
 83     while(T--)
 84     {
 85         clr();
 86         while(1)
 87         {
 88             scanf("%s",si);
 89             if(si[0]==‘0‘) break;
 90             else if(si[1]==‘p‘) a[si[0]-‘0‘]--;
 91             else if(si[1]==‘s‘) a[si[0]-‘0‘+9]--;
 92             else if(si[1]==‘m‘) a[si[0]-‘0‘+18]--;
 93             else if(si[0]==‘E‘) a[28]--;
 94             else if(si[0]==‘S‘) a[29]--;
 95             else if(si[0]==‘W‘) a[30]--;
 96             else if(si[0]==‘N‘) a[31]--;
 97             else if(si[0]==‘B‘) a[32]--;
 98             else if(si[0]==‘F‘) a[33]--;
 99             else if(si[0]==‘Z‘) a[34]--;
100         }
101         while(1)
102         {
103             scanf("%s",si);
104             if(si[0]==‘0‘) break;
105             else if(si[1]==‘p‘) dora[si[0]-‘0‘]=2;
106             else if(si[1]==‘s‘) dora[si[0]-‘0‘+9]=2;
107             else if(si[1]==‘m‘) dora[si[0]-‘0‘+18]=2;
108             else if(si[0]==‘E‘) dora[28]=2;
109             else if(si[0]==‘S‘) dora[29]=2;
110             else if(si[0]==‘W‘) dora[30]=2;
111             else if(si[0]==‘N‘) dora[31]=2;
112             else if(si[0]==‘B‘) dora[32]=2;
113             else if(si[0]==‘F‘) dora[33]=2;
114             else if(si[0]==‘Z‘) dora[34]=2;
115         }
116         kokushi();//国士无双
117         chitoi();//七对子
118         for(int i=1;i<=34;i++)
119         {
120             for(int j=0;j<=4;j++)
121             {
122                 for(int k=0;k<=4;k++)
123                 {
124                     for(int l=0;l<=2;l++)
125                     {
126                         for(int m=0;m<=2;m++)
127                         {
128                             if(!dp[i][j][0][k][l][m]&&!dp[i][j][1][k][l][m]) continue;
129                             if(a[i]-k>=2) dm(dp[i][j][1][k+2][l][m],dp[i][j][0][k][l][m]/c[a[i]][k]*c[a[i]][k+2]*dora[i]*dora[i]);
130                             //雀头
131                             if(j<4)
132                             {
133                                 if(a[i]-k>=3) d01(o) dm(dp[i][j+1][o][k+3][l][m],dp[i][j][o][k][l][m]/c[a[i]][k]*c[a[i]][k+3]*dora[i]*dora[i]*dora[i]);
134                                 //刻子
135                                 if((!tail[i])&&a[i]-k>0&&a[i+1]-l>0&&a[i+2]-m>0&&l!=2&&m!=2)
136                                     d01(o) dm(dp[i][j+1][o][k+1][l+1][m+1],dp[i][j][o][k][l][m]/c[a[i]][k]*c[a[i]][k+1]*dora[i]/c[a[i+1]][l]*c[a[i+1]][l+1]*dora[i+1]/c[a[i+2]][m]*c[a[i+2]][m+1]*dora[i+2]);
137                                 //顺子
138                             }
139                             dm(dp[i+1][j][0][l][m][0],dp[i][j][0][k][l][m]);
140                             dm(dp[i+1][j][1][l][m][0],dp[i][j][1][k][l][m]);
141                             //转移
142                             if(j==4) dg[i]=max(dg[i],dp[i][j][1][k][l][m]);
143                         }
144                     }
145                 }
146             }
147         }
148         for(int i=1;i<=35;i++) ans=max(ans,dg[i]);
149         printf("%lld\n",ans);
150     }
151     return 0;
152 }

Orz

原文地址:https://www.cnblogs.com/rikurika/p/gxoi2019_1.html

时间: 2024-10-12 09:00:05

[GXOI/GZOI2019]宝牌一大堆(dp)的相关文章

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

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

习题:宝牌一大堆(DP&amp;卡常)

题目 传送门 思路 这道题主要是状态不好想和题目不好理解 如果你和笔者一样没有接触过麻将 我们首先将整个麻将进行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_{

网络麻将的宝牌设计

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

题解-GXOI/GZOI2019 特技飞行

Problem loj3085 bzoj不放题面差评 题意概要:给出两条竖直直线,再给出 \(n\) 架飞机的初始航线:一条接通这两条直线的线段,保证航线交点不在两条直线上.现要求安排所有飞机在航线相交处做特技: 擦身而过:两架飞机按原方向线路继续前进,一次得分 \(b\) 对向交换:两架飞机交换线路继续前进,一次得分 \(a\) 另外,给定 \(k\) 个边界与坐标轴成 \(45°\)角 的正方形,若一次特技被至少一个正方形囊括,则总得分加 \(c\) 现要求决策每次相遇做的特技,求最大/最小

Loj #3085. 「GXOI / GZOI2019」特技飞行

Loj #3085. 「GXOI / GZOI2019」特技飞行 题目描述 公元 \(9012\) 年,Z 市的航空基地计划举行一场特技飞行表演.表演的场地可以看作一个二维平面直角坐标系,其中横坐标代表着水平位置,纵坐标代表着飞行高度. 在最初的计划中,这 \(n\) 架飞机首先会飞行到起点 \(x = x_{st}\) 处,其中第 \(i\) 架飞机在起点处的高度为 \(y_{i,0}\).它们的目标是终点 \(x = x_{ed}\) 处,其中第 \(i\) 架飞机在终点处的高度应为 \(y

[JLOI2013]卡牌游戏 概率DP

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

[LuoguP5305][GXOI/GZOI2019]旧词 (树链剖分)

[GXOI/GZOI2019]旧词 (树链剖分) 题面 给定一棵 \(n\)个点的有根树,节点标号 \([1,n]\),1号节点为根. 给定常数\(k\) 给定\(Q\)个询问,每次询问给定\(x,y\),求:\(\sum_{i=1}^x \mathrm{deep}(\mathrm{lca}(i,y)) \mod 998244353\) 分析 此题为[BZOJ3626] [LNOI2014]LCA(树链剖分)的加强版. 考虑原来的做法(k=1):我们把i到根的路径上所有点+1,y到根路径上的权值

hdoj1584 蜘蛛牌 区间型DP

题目链接 分析: f[i][j] 表示 把一串牌 牌 i 到 j 摞为一摞 时花费最少的步数. d[i][j] 表示把牌 i 挪到牌 j 上时需要走的步数(最初给的状态). 以一串牌 3~8 为例, 我们需要把牌 3 放到牌 4 上 , 而在最优的移动方案下, 牌 4 的位置不确定, 所以我们枚举牌 4 所在的位置(因为一共10张牌, 枚举是可以的), 这样得出状态转移方程: f[3][8] = min(f[3][8], f[4][k] + f[k][8] + d[3][k]); ( 4 <=

nyoj 712 探 寻 宝 藏(双线dp 第六届河南省程序设计大赛)

探 寻 宝 藏 时间限制:1000 ms  |  内存限制:65535 KB 难度:5 描述 传说HMH大沙漠中有一个M*N迷宫,里面藏有许多宝物.某天,Dr.Kong找到了迷宫的地图,他发现迷宫内处处有宝物,最珍贵的宝物就藏在右下角,迷宫的进出口在左上角.当然,迷宫中的通路不是平坦的,到处都是陷阱.Dr.Kong决定让他的机器人卡多去探险. 但机器人卡多从左上角走到右下角时,只会向下走或者向右走.从右下角往回走到左上角时,只会向上走或者向左走,而且卡多不走回头路.(即:一个点最多经过一次).当