[ ZJOI 2006 ] Mahjong
\(\\\)
\(Description\)
现有权值分别为\(1\text~100\)的\(100\)种牌,分别给出每种排的张数\(A_i\),试判断能否胡牌,胡牌需要将所有牌不重不漏地分成以下几类:
- 三张或四张相同的牌
- 权值连续的三张牌
- 两张相同的牌,这一类必须要有,而且只能有一个
一组数据共需要\(N\)次判断,胡了输出“\(Yes\)”,否则输出"\(No\)"。
- \(N\in [1,100]\),\(A_i\in [0,100]\)
\(\\\)
\(Solution\)
没有写贪心,但是找到了两种\(DP\)的方式,按照便于理解的顺序介绍:
\(\\\)
\(\text O(N\times 100^3)\):
注意到每张牌只会影响到周围至多三种牌的出牌,而两两关系又可以互相导出,所以设\(f[i][j][k][0/1]\)表示,当前处理到第\(i\)种牌,第\(i-1\)种牌出了\(j\)张,第\(i\)种牌出了\(k\)张,有\(/\)没有计算过对子,其余更靠前的牌全部出完是否可行,至于保证全部出完,后面再给证明。有显然的初始化\(f[0][0][0][0]=1\)。
对于对子有特殊转移:
if(k>1) f[i][j][k][1]|=f[i][j][k-2][0];
当前成立一个三张相同的牌,注意对子部分只能从相同的种类转移:
if(k>2) {f[i][j][k][1]|=f[i][j][k-3][1];f[i][j][k][0]|=f[i][j][k-3][0];}
当前成立一个四张相同的牌,讨论相同:
if(k>3) {f[i][j][k][1]|=f[i][j][k-4][1];f[i][j][k][0]|=f[i][j][k-4][0];}
注意到上面三种只对当前有要求,而成立三张连续的将会关系到前后的答案,因为确定后面的影响很不方便,我们不妨设三张连续的当前位置只考虑以当前的牌结束的部分。同样的,因为不便于考虑出多少组三张连续的牌,但注意到转移过程中会逐层转移自之前的情况,所以最优组合方案无需枚举,我们直接令当前的牌全部用于出连续三个一组的类型。同时为了保证除掉当前两种前面的全部都出完,所以向前找状态时默认第\(i-2\)种牌全部出完:
if(j>=k&&a[i-2]>=k){
f[i][j][k][1]|=f[i-1][a[i-2]-k][j-k][1];
f[i][j][k][0]|=f[i-1][a[i-2]-k][j-k][0];
}
答案即为\(f[100][A_{99}][A_{100}][1]\)是否为真。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 1010
#define R register
#define gc getchar
using namespace std;
int a[N];
bool f[N][N][N][2];
inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
int main(){
int t=rd();
while(t--){
for(R int i=1;i<=100;++i) a[i]=rd();
memset(f,0,sizeof(f)); f[0][0][0][0]=1;
for(R int i=1;i<=100;++i)
for(R int j=0;j<=100;++j)
for(R int k=0;k<=100;++k){
if(k>1)f[i][j][k][1]|=f[i][j][k-2][0];
if(k>2){f[i][j][k][1]|=f[i][j][k-3][1];f[i][j][k][0]|=f[i][j][k-3][0];}
if(k>3){f[i][j][k][1]|=f[i][j][k-4][1];f[i][j][k][0]|=f[i][j][k-4][0];}
if(j>=k&&a[i-2]>=k){
f[i][j][k][1]|=f[i-1][a[i-2]-k][j-k][1];
f[i][j][k][0]|=f[i-1][a[i-2]-k][j-k][0];
}
}
puts(f[100][a[99]][a[100]][1]?"Yes":"No");
}
return 0;
}
\(\\\)
\(\Theta(N\times 100\times 3^3)\):
思路相同,但转化成了分别讨论相同的牌和连续的牌不同的出法。
注意到其实两类之间是可以互相转换的,具体地说,对于连续三张的出法,一样的三张牌的组成超过三组就是没意义的了,因为它完全可以拆成三张牌独立出各自的一套,剩下的再组成三张连续出的方法,例如五组相同的组成\((5\times 3)\)就可以拆成三种牌各自出一套再加上两组\((3\times 3\times 1+2\times3)\),或三种牌各自出一套四张一族的,再加上一组\((3\times 4\times 1+1\times 3)\)。我们发现,去掉所有的连续三张一组的部分以及一对的部分,如果剩下的每一类牌的数量都可以表示成\(3x+4y\)的形式,那么这个方案就是合法的。
于是状态就能神奇的设计为(以下称相同的牌三个或四个一组的为一套,称连续三张一组的为顺),\(f[i][0/1/2][0/1/2][0/1]\)表示当前处理到第\(i\)张牌,以第\(i-1\)张牌开始的顺出了\(0/1/2\)个,以第\(i\)张牌开始的顺出了\(0/1/2\)个,第\(i-1\)种和第\(i\)种其他的牌全部出成套,没出\(/\)出过对子的状态是否合法。同样有显然初始化\(f[0][0][0][0]=1\),注意预处理出所有能表示成形如\(3x+4y\)形式的数。
转移考虑以第\(i+1\)个数为开始出了\(s\)个顺,注意要从合法的状态扩展,此时第\(i+1\)种牌剩余出成套的数即为\(A_{i+1}-j-k-s\),若该数字查表合法则转移。
关于\(0/1\)状态间的转移,只需在每次枚举到\(0\)状态之后,讨论两种扩展即可。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 110
#define R register
#define gc getchar
using namespace std;
int a[N]={0};
bool val[100]={1},f[N][3][3][2];
inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
int main(){
int t=rd();
for(R int i=0;i<=35;++i)
for(R int j=0;j<=25;++j)
if(3*i+4*j<=100) val[3*i+4*j]=1;
while(t--){
memset(f,0,sizeof(f));
f[0][0][0][0]=1;
for(R int i=1;i<=100;++i) a[i]=rd();
for(R int i=0;i<=99;++i)
for(R int j=0;j<=2;++j)
for(R int k=0;k<=2;++k){
if(f[i][j][k][0])
for(R int s=0;s<=2;++s){
int num=a[i+1]-j-k-s;
if(num>=0&&val[num]) f[i+1][k][s][0]=1;
if(num-2>=0&&val[num-2]) f[i+1][k][s][1]=1;
}
if(f[i][j][k][1])
for(R int s=0;s<=2;++s){
int num=a[i+1]-j-k-s;
if(num>=0&&val[num])f[i+1][k][s][1]=1;
}
}
puts(f[100][0][0][1]?"Yes":"No");
}
return 0;
}
[ ZJOI 2006 ] Mahjong的更多相关文章
- [ZJOI 2006]超级麻将
Description Input 第一行一个整数N(N<=100),表示玩了N次超级麻将. 接下来N行,每行100个数a1..a100,描述每次玩牌手中各种牌的数量.ai表示数字为i的牌有ai ...
- [ZJOI 2006]书架
Description 小T有一个很大的书柜.这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列.她用1到n的正整数给每本书都编了号. 小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下 ...
- [ZJOI 2006]物流运输
Description 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格 ...
- 解题:ZJOI 2006 皇帝的烦恼
禁止DP,贪心真香 有一个比较明显的贪心思路是让每个人和距离为$2$(隔着一个人)的人尽量用一样的,这样只需要扫一遍然后对每对相邻的人之和取最大值即可.但是当人数为奇数时这样就会出锅,因为最后一个人和 ...
- 解题:ZJOI 2006 书架
题面 学习了如何在维护序列的平衡树上查找某个数:按初始的顺序定个权值,然后每次找那个权值的DFS序即可.具体实现就是不停往上跳,然后是父亲的右儿子就加上父亲的左儿子,剩下的就是继续熟悉无旋树堆 #in ...
- 解题:ZJOI 2006 游戏排名系统
题面 跟i207M学了学重载运算符后找前驱后继,然后就是练练无旋树堆 #include<map> #include<cstdio> #include<string> ...
- [BZOJ1003](ZJOI 2006) 物流运输trans
[题目描述] 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟 ...
- 【ZJOI 2006】 物流运输
[题目链接] 点击打开链接 [算法] 令cost(i,j) = 第i天到第j天走相同的路线,路线长度的最小值 那么,只需筛选出第i天到第j天可以装卸货物的码头,然后将这些码头之间连边,跑弗洛伊德(或其 ...
- 洛谷 P2585 [ ZJOI 2006 ] 三色二叉树 —— 树形DP
题目:https://www.luogu.org/problemnew/show/P2585 首先,三色其实记录两种状态:是绿色,不是绿色 即可,因为红蓝可以随意取反: 一开始因为懒得还原出树,所以写 ...
随机推荐
- [BZOJ2843] 极地旅行社(LCT)
传送门 模板. ——代码 #include <cstdio> #include <iostream> #define N 300001 #define get(x) (son[ ...
- Linux下汇编语言学习笔记73 ---
这是17年暑假学习Linux汇编语言的笔记记录,参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译<汇编语言基于Linux环境>的书,喜欢看原版书的同学可以看<Ass ...
- MySQL大小写问题的简单说明(关键字/函数/表名)(转)
MySQL语句中字母大小写规则随着语句元素的不同而变化,同时还要取决于MySQL服务器主机上的操作系统. SQL关键字与函数名 关键字和函数名不区分字母的大小写.如.abs.bin.now.versi ...
- Java设计模式补充:回调模式、事件监听器模式、观察者模式(转)
一.回调函数 为什么首先会讲回调函数呢?因为这个是理解监听器.观察者模式的关键. 什么是回调函数 所谓的回调,用于回调的函数. 回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数. ...
- git SSL certificate problem: unable to get local issuer certificate
cmd 命令行中输入 git config --global http.sslVerify false 之后再进行操作
- 十进制浮点数转换成IEEE754标准的32浮点数的二进制格式
参考: http://jimmygod.blog.163.com/blog/static/43511339200792605627411/ http://blog.csdn.net/archersab ...
- 1.4-动态路由协议OSPF⑧
OSPF认证(保证寻路协议级别的网络安全) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 按照参与认证的成员,进行分类: 1:链路认证(参与认证的成员 ...
- 阿里云部署Docker(2)
之前有一篇文章讲过在阿里云中安装Docker,相对来说那个是安装.可是安装完之后我们通常会碰到问题. 今天我给大家记录一下我的新的解决过程. 环境还是ubuntu12.04.如果我们已经把内核升级到了 ...
- react 使用
我的有道云笔记 React 事件: 1.不能使用 return false; 来阻止元素的默认行为.需要在方法的最前面使用 e.preventDefault() 来阻止元素的默认行为(例如:a 标签的 ...
- 重写description方法
//重写description方法 //description建议大家在实际开发中都要重写这种方法.然后将类中有意义的成员变量打印出来,这样很方便我们调试程序 -(NSString *)descrip ...