题面

好像ZJOI也考了一道麻将, 这是要发扬中华民族的赌博传统吗???

暴搜都不会打, 看到题目就自闭了, 考完出来之后看题解, \(dp\), 可惜自己想不出来...

对于国士无双(脑子中闪过了韩信)和七对子进行特判, 国士无双枚举哪张牌选两张即可, 七对子的话对于每种牌, 如果该种牌可以凑对子, 就将它凑对子对答案的贡献算出来, 排序后贪心地选七个最大的即可

国士无双

		for(int i = 1; i <= 13; i++)
{
long long tmp = 1;
for(int j = 1; j <= 13; j++)
{
if(i == j)
{
if(a[gs[j]] < 2) tmp = 0;
else tmp *= c[a[gs[j]]][2] * (b[gs[j]] ? 4 : 1);
}
else
{
if(a[gs[j]] < 1) tmp = 0;
else tmp *= c[a[gs[j]]][1] * (b[gs[j]] ? 2 : 1);
}
}
ans = max(ans, tmp * 13);
}

七对子

		cnt = 0;
for(int i = 1; i <= 34; i++) if(a[i] >= 2) tp[++cnt] = c[a[i]][2] * (b[i] ? 4 : 1);
if(cnt >= 7)
{
sort(tp + 1, tp + cnt + 1);
long long tmp = 1;
for(int i = cnt; i > cnt - 7; i--) tmp *= tp[i];
ans = max(ans, tmp * 7);
}

设\(f[i][j][k][l][0/1]\)代表已经选完了前\(i\)种牌, 有\(j\)个顺子/刻子/杠子, 以\(i\) - \(1\)开头的顺子有\(k\)个, 以\(i\)开头的顺子有\(l\)个, 有没有选雀头的最大分数.

其中, \(k\)和\(l\)都应该不大于2, 因为当\(k\)或\(l\)中有一个大于2时, 我们就可以把些看做三个杠子或者三个刻子, 这可以直接记录到\(j\)里面去.

所以我们得到下面几个转移式(这个题顺推似乎要好推一些(感谢题解, 滑稽)):

		for(int i = 0; i < 34; i++)
{
for(int j = 0; j <= 4; j++)
{
for(int k = 0; k < 3 && j + k <= 4; k++)
{
if(k && (i == 9 || i == 18 || i >= 27)) break; //注意, 当i=9或i=18或i>=27时不能够由这种牌为开头组成顺子
for(int l = 0; l < 3 && j + k + l <= 4; l++)
{
if(l && (i == 9 || i == 18 || i >= 27)) break; //同上
if(!f[i][j][k][l][0] && !f[i][j][k][l][1]) continue;
for(int x = k + l; x <= a[i + 1]; x++)
{
long long tmp = c[a[i + 1]][x] * (b[i + 1] ? (1 << x) : 1);
if(x == 4 && !k && !l && j <= 3)
{
f[i + 1][j + 1][0][0][0] = max(f[i + 1][j + 1][0][0][0], f[i][j][k][l][0] * tmp);
f[i + 1][j + 1][0][0][1] = max(f[i + 1][j + 1][0][0][1], f[i][j][k][l][1] * tmp);
}
//凑一个杠子, 条件是x - k - l >= 4, 又由于x <= 4, 所以x = 4, 并且j + 1 <= 4, 这个1是凑出来的杠子, 最后的面子和杠子总和不能超过4, 由于l, k均为0, 所以第i + 1个的情况也为0, 没有以i为开头的顺子, 也没有以i + 1为开头的顺子, 全拿去凑杠子去了
if(x - k - l >= 3 && j + x - 2 <= 4)
{
f[i + 1][j + k + 1][l][x - k - l - 3][0] = max(f[i + 1][j + k + 1][l][x - k - l - 3][0], f[i][j][k][l][0] * tmp);
f[i + 1][j + k + 1][l][x - k - l - 3][1] = max(f[i + 1][j + k + 1][l][x - k - l - 3][1], f[i][j][k][l][1] * tmp);
}
//凑一个刻子, 那么应该满足的条件是x - k - l >= 3, 即在满足k与l两个要求后剩余的牌还能够拿出来凑成一个杠子, 那么最后到第i + 1种牌时以i + 1为开头的数量应该是x - k - l - 3, 凑完顺子和刻子之后剩下来的当面子, 此时杠子面子数量总和为j + k + 1
if(x - k - l >= 2 && j + x - 2 <= 4)
f[i + 1][j + k][l][x - k - l - 2][1] = max(f[i + 1][j + k][l][x - k - l - 2][1], f[i][j][k][l][0] * tmp);
//拿当前牌凑一个雀头, 前提条件是没有雀头, 类比上面的情况分析可知条件分别为x - k - l >= 2, j + x - 2 <= 4, 这里需要注意一下的是由于雀头不算在杠子和面子的总和中, 所以新增的数量便是k, 以i + 1为开头的顺子数量为x - k - l - 2
if(j + x <= 4 && x - k - l < 3)
{
f[i + 1][j + k][l][x - k - l][0] = max(f[i + 1][j + k][l][x - k - l][0], f[i][j][k][l][0] * tmp);
f[i + 1][j + k][l][x - k - l][1] = max(f[i + 1][j + k][l][x - k - l][1], f[i][j][k][l][1] * tmp);
}
//什么都不凑只拿来做顺子开头的情况, 自己类比上面分析一下即可
}
}
}
}
}

最后比较一下, 输出三种中的最大值即可, 下面是完整代码

完整代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std; int c[5][5], a[40], T, cnt;
int gs[14] = { 0, 1, 9, 10, 18, 19, 27, 28, 29, 30, 31, 32, 33, 34 };
long long tp[40], f[40][5][3][3][2];
bool b[40]; 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;
} void check1(char c, char ch, int pos) { if(c == ch) a[pos]++; } void check2(char c, char ch, int pos) { if(c == ch) b[pos] = 1; } int main()
{
T = read();
for(int i = 0; i <= 4; i++)
for(int j = 0; j <= i; j++)
c[i][j] = (j ? c[i - 1][j] + c[i - 1][j - 1] : 1);
while(T--)
{
memset(a, 0, sizeof(a)); memset(f, 0, sizeof(f)); memset(b, 0, sizeof(b));
while(1)
{
string s; cin>>s;
if(s[0] == '0') break;
check1(s[0], 'E', 28); check1(s[0], 'S', 29); check1(s[0], 'W', 30); check1(s[0], 'N', 31); check1(s[0], 'Z', 32); check1(s[0], 'B', 33);
check1(s[0], 'F', 34); check1(s[1], 'm', s[0] - '0'); check1(s[1], 'p', s[0] - '0' + 9); check1(s[1], 's', s[0] - '0' + 18);
}
while(1)
{
string s; cin>>s;
if(s[0] == '0') break;
check2(s[0], 'E', 28); check2(s[0], 'S', 29); check2(s[0], 'W', 30); check2(s[0], 'N', 31); check2(s[0], 'Z', 32); check2(s[0], 'B', 33);
check2(s[0], 'F', 34); check2(s[1], 'm', s[0] - '0'); check2(s[1], 'p', s[0] - '0' + 9); check2(s[1], 's', s[0] - '0' + 18);
}
for(int i = 1; i <= 34; i++) a[i] = 4 - a[i];
long long ans = 0;
for(int i = 1; i <= 13; i++)
{
long long tmp = 1;
for(int j = 1; j <= 13; j++)
{
if(i == j)
{
if(a[gs[j]] < 2) tmp = 0;
else tmp *= c[a[gs[j]]][2] * (b[gs[j]] ? 4 : 1);
}
else
{
if(a[gs[j]] < 1) tmp = 0;
else tmp *= c[a[gs[j]]][1] * (b[gs[j]] ? 2 : 1);
}
}
ans = max(ans, tmp * 13);
}
cnt = 0;
for(int i = 1; i <= 34; i++) if(a[i] >= 2) tp[++cnt] = c[a[i]][2] * (b[i] ? 4 : 1);
if(cnt >= 7)
{
sort(tp + 1, tp + cnt + 1);
long long tmp = 1;
for(int i = cnt; i > cnt - 7; i--) tmp *= tp[i];
ans = max(ans, tmp * 7);
}
f[0][0][0][0][0] = 1;
for(int i = 0; i < 34; i++)
{
for(int j = 0; j <= 4; j++)
{
for(int k = 0; k < 3 && j + k <= 4; k++)
{
if(k && (i == 9 || i == 18 || i >= 27)) break;
for(int l = 0; l < 3 && j + k + l <= 4; l++)
{
if(l && (i == 9 || i == 18 || i >= 27)) break;
if(!f[i][j][k][l][0] && !f[i][j][k][l][1]) continue;
for(int x = k + l; x <= a[i + 1]; x++)
{
long long tmp = c[a[i + 1]][x] * (b[i + 1] ? (1 << x) : 1);
if(x == 4 && !k && !l && j <= 3)
{
f[i + 1][j + 1][0][0][0] = max(f[i + 1][j + 1][0][0][0], f[i][j][k][l][0] * tmp);
f[i + 1][j + 1][0][0][1] = max(f[i + 1][j + 1][0][0][1], f[i][j][k][l][1] * tmp);
}
if(x - k - l >= 3 && j + x - 2 <= 4)
{
f[i + 1][j + k + 1][l][x - k - l - 3][0] = max(f[i + 1][j + k + 1][l][x - k - l - 3][0], f[i][j][k][l][0] * tmp);
f[i + 1][j + k + 1][l][x - k - l - 3][1] = max(f[i + 1][j + k + 1][l][x - k - l - 3][1], f[i][j][k][l][1] * tmp);
}
if(x - k - l >= 2 && j + x - 2 <= 4)
f[i + 1][j + k][l][x - k - l - 2][1] = max(f[i + 1][j + k][l][x - k - l - 2][1], f[i][j][k][l][0] * tmp);
if(j + x <= 4 && x - k - l < 3)
{
f[i + 1][j + k][l][x - k - l][0] = max(f[i + 1][j + k][l][x - k - l][0], f[i][j][k][l][0] * tmp);
f[i + 1][j + k][l][x - k - l][1] = max(f[i + 1][j + k][l][x - k - l][1], f[i][j][k][l][1] * tmp);
}
}
}
}
}
}
ans = max(ans, f[34][4][0][0][1]);
printf("%lld\n", ans);
}
return 0;
}

复杂度自己算吧...

[luogu 5301][bzoj 5503] [GXOI/GZOI2019] 宝牌一大堆的更多相关文章

  1. 【BZOJ5503】[GXOI/GZOI2019]宝牌一大堆(动态规划)

    [BZOJ5503][GXOI/GZOI2019]宝牌一大堆(动态规划) 题面 BZOJ 洛谷 题解 首先特殊牌型直接特判. 然后剩下的部分可以直接\(dp\),直接把所有可以存的全部带进去大力\(d ...

  2. [LOJ3084][GXOI/GZOI2019]宝牌一大堆——DP

    题目链接: [GXOI/GZOI2019]宝牌一大堆 求最大值容易想到$DP$,但如果将$7$种和牌都考虑进来的话,$DP$状态不好设,我们将比较特殊的七小对和国士无双单独求,其他的进行$DP$. 观 ...

  3. P5301 [GXOI/GZOI2019]宝牌一大堆

    题目地址:P5301 [GXOI/GZOI2019]宝牌一大堆 这里是官方题解(by lydrainbowcat) 部分分 直接搜索可以得到暴力分,因为所有和牌方案一共只有一千万左右,稍微优化一下数据 ...

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

    luogu     bzoj 这个麻将题还算挺友善的,比隔壁zjoi的要好得多... 比较正常的做法是五维dp 但事实上六维dp也是完全不会被卡的 七对子选权值最高的七个,国士无双直接$13^2$暴力 ...

  5. luogu P5301 [GXOI/GZOI2019]宝牌一大堆

    传送门 wdnm又是打麻将 首先国土无双可以直接枚举哪种牌用了\(2\)次算贡献,然后\(7\)个对子可以把每种牌的对子贡献排序,取最大的\(7\)个,剩下的牌直接暴力枚举是不行的,考虑dp,设\(f ...

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

    原题传送门 首先先要学会麻将,然后会发现就是一个暴力dp,分三种情况考虑: 1.非七对子国士无双,设\(dp_{i,j,k,a,b}\)表示看到了第\(i\)种牌,一共有\(j\)个\(i-1\)开头 ...

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

    这道题除了非常恶心以外也没有什么非常让人恶心的地方 当然一定要说有的话还是有的,就是这题和咱 ZJOI 的 mahjong 真的是好像的说~ 于是就想说这道题出题人应该被 锕 掉 noteskey 整 ...

  8. [GXOI/GZOI2019]宝牌一大堆

    感觉比ZJOI的麻将要休闲很多啊. 这个题就是一个最优化问题,没有面子的特殊牌型可以直接用复杂度较低的贪心判掉. 有面子的话就是一个经典dp.(曾经还在ZJOI写过这个毒瘤东西 大概就是存一下对子,面 ...

  9. [luogu 5300][bzoj 5502] [GXOI/GZOI2019] 与或和

    题面 思路还是挺容易想的, 只是由于我还是太\(naive\)了一点不会做只会打暴力吧...... 题目要我们求所有子矩阵的\(and\)值之和与\(or\)值之和, 一看之下似乎不好入手, 我们慢慢 ...

随机推荐

  1. 20155312张竞予 20170510实践一:在IDEA中以TDD的方式对String类和Arrays类进行学习

    实践题目 在IDEA中以TDD的方式对String类和Arrays类进行学习 测试相关方法的正常,错误和边界情况 String类 charAt split Arrays类 sort binarySea ...

  2. 经典矩阵快速幂之二-----hdu2157(走k步到

    题意:(中问题,题意很简单 思路:a走k步到b,其实就是A^k,ans.mat[a][b]就是答案. 其实就是离散的邻接矩阵那个P(不想证明,逃 #include<cstdio> #inc ...

  3. 2019.01.06 vijos lxhgww的奇思妙想(长链剖分)

    传送门 长链剖分模板题. 题意简述:允许O(nlogn)O(nlog_n)O(nlogn​)预处理,让你支持O(1)O(1)O(1)查找任意一个点的kkk级祖先. 思路:因为要O(1)O(1)O(1) ...

  4. mysql 设置外键 四大属性 CASCADE SET NULL NO ACTION RESTRICT 理解

    CASCADE:主表delete.update的时候,从表会delete.update掉关联记录: SET NULL:主表delete.update的时候,从表会将关联记录的外键字段所在列设为null ...

  5. springboot深入学习(三)-----docker

    一.spring data思路 spring data使用统一的api来对各种数据库存储技术进行数据访问操作提供了支持,包括oracle.mysql.redis.mongoDB等等.主要是通过spri ...

  6. tomcat7 安装 windows 服务

    tomcat 可以安装成windows 服务,这样 每次启动就不需要启动tomcat了. 具体配置: 1.修改 service.bat 在行首添加 set "JAVA_HOME=E:\jdk ...

  7. UVa 10828 Back to Kernighan-Ritchie (数学期望 + 高斯消元)

    题意:给定一个 n 个结点的有向图,然后从 1 结点出发,从每个结点向每个后继结点的概率是相同的,当走到一个没有后继结点后,那么程序终止,然后问你经过每个结点的期望是次数是多少. 析:假设 i 结点的 ...

  8. nginx proxy_pass后的url加不加/的区别

    在nginx中配置proxy_pass时,当在后面的url加上了/,相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;如果没有/,则会把匹配的路径部分也给代理走. 下面四种 ...

  9. Language Oriented Programming:下一代编程样式 Part I (翻译)

    原文信息 原文地址 作者信息 Sergey Dmitriev JetBrains Sergey Dmitriev is the cofounder and CEO of JetBrains Inc., ...

  10. std::string的find问题研究

    https://files-cdn.cnblogs.com/files/aquester/std之string的find问题研究.pdf 目录 目录 1 1. 前言 1 2. find字符串 1 3. ...