一道清真好题,只是之前没有接触过,才感觉非常困难。
感谢yn大佬。

我的做法是dp套dp的做法。
首先如果你有一个麻将自动机,
这个自动机有若干节点,以及节点之间的转移边,以及一个起始状态节点,和一个终止状态集合。

终止状态集合中的节点代表胡了。
你可以给这个自动机新添加一种花色,麻将的数量在0~4之间,然后沿着转移边走到下一个状态。

如果你有这样一个自动机,那么考虑一个dp,\(f[i][j][k]\)表示考虑了前\(i\)种花色,选了\(j\)张麻将,在自动机的\(k\)节点时的无序集合数。
然后一个一定长度的无序集合,映射到相同个数的排列数。
就可以愉快的算出答案了。

当然期望按照套路转化成还未结束的概率和。

丑陋的代码

#include <bits/stdc++.h>using namespace std;typedef long long i64;
const int N=105;
const int MM=4000;
const int M=998244353;namespace{void Addt(int &x,int y){(x+=y)>=M?x-=M:0;}int mul(int x,int y){return (i64)x*y%M;}int fp(int x,int y){int ret=1;for (; y; y>>=1,x=mul(x,x))if (y&1) ret=mul(ret,x);return ret;}
}
struct st{int a[3][3];st(){}st(int x){for (int i=0; i<3; ++i)for (int j=0; j<3; ++j)a[i][j]=x;}st operator +(const st &_) const{st ret;for (int i=0; i<3; ++i)for (int j=0; j<3; ++j)ret.a[i][j]=max(a[i][j],_.a[i][j]);return ret;}st operator +(const int &num) const{st ret(-1);for (int i=0; i<3; ++i)for (int j=0; j<3; ++j)for (int k=0; k<3&&i+j+k<=num; ++k){if (a[i][j]==-1) continue;ret.a[j][k]=min(max(ret.a[j][k],a[i][j]+(num-k-i-j)/3+i),4);}return ret;}bool operator <(const st &_) const{for (int i=0; i<3; ++i)for (int j=0; j<3; ++j)if (a[i][j]!=_.a[i][j]) return a[i][j]<_.a[i][j];return 0;}bool operator !=(const st &_) const{for (int i=0; i<3; ++i)for (int j=0; j<3; ++j)if (a[i][j]!=_.a[i][j]) return 1;return 0;}
};
map<st,int> mst;
struct mj{st f,g;int cnt;//number of different dui zibool operator <(const mj &_) const{if (cnt!=_.cnt) return cnt<_.cnt;if (f!=_.f) return f<_.f;return g<_.g;}mj operator +(const int &a) const{mj ret=*this;if (a>=2){ret.cnt=min(ret.cnt+1,7);ret.g=(ret.g+a)+(ret.f+(a-2));}else ret.g=ret.g+a;ret.f=ret.f+a;return ret;}bool ri(){if (cnt>=7) return 1;//carereturn g.a[0][0]>=4;}
}m_j[MM];
map<mj,int> mmj;
int tots,totm;
void Dfscomplicated(const st &&s){if (mst.count(s)) return;mst[s]=++tots;for (int i=0; i<=4; ++i){Dfscomplicated(s+i);}
}
void Dfshdaewr(const mj &&m){//getchar();if (mmj.count(m)) return;//cerr<<"Dfshdaewr"<<" "<<totm<<" "<<m.cnt<<" "<<mst[m.f]<<" "<<mst[m.g]<<" "<<(mmj.find(m)==mmj.end())<<endl;mmj[m]=++totm;//cerr<<mmj[m]<<endl;m_j[totm]=m;for (int i=0; i<=4; ++i){Dfshdaewr(m+i);}
}
st zst(){st ret=st(-1);ret.a[0][0]=0;return ret;
}
mj zmj(){mj ret;ret.f=zst();ret.g=st(-1);ret.cnt=0;return ret;
}
int binomial[5][5],trans[MM][5];
void Prework(){for (int i=0; i<=4; ++i){binomial[i][0]=1;for (int j=1; j<=i; ++j)Addt(binomial[i][j]=binomial[i-1][j-1],binomial[i-1][j]);}//cerr<<"???"<<endl;Dfscomplicated(zst());//cerr<<"????"<<tots<<endl;Dfshdaewr(zmj());//cerr<<"?????"<<totm<<endl;for (int i=1; i<=totm; ++i)for (int j=0; j<=4; ++j){trans[i][j]=mmj[m_j[i]+j];//cerr<<i<<" "<<j<<" "<<trans[i][j]<<endl;}//cerr<<"Preworkend"<<endl;
}
int n,usd[N],f[N][N*4][MM];
int main(){cin>>n;for (int i=1; i<=13; ++i){int x,y;cin>>x>>y;++usd[x];}Prework();//cerr<<"!!!!!"<<binomial[0][0]<<endl;f[0][0][1]=1;for (int i=0; i<n; ++i)for (int j=0; j<=i*4; ++j)for (int d=1; d<=totm; ++d)if (f[i][j][d]){//cerr<<i<<" "<<j<<" "<<d<<" "<<f[i][j][d]<<" "<<m_j[d].ri()<<endl;//getchar();int tmp=f[i][j][d];for (int k=usd[i+1]; k<=4; ++k){//cerr<<"trans:"<<k<<" "<<trans[d][k]<<endl;Addt(f[i+1][j+k][trans[d][k]],mul(tmp,binomial[4-usd[i+1]][k-usd[i+1]]));}}//cerr<<"????"<<endl;int ans=0;for (int j=13; j<=n*4; ++j){int sum=0;int nend=0;for (int i=1; i<=totm; ++i){Addt(sum,f[n][j][i]);//if (f[n][j][i]) cerr<<j<<" "<<i<<" "<<f[n][j][i]<<" "<<ans<<" "<<sum<<" "<<nend<<endl;if (!m_j[i].ri()) Addt(nend,f[n][j][i]);}Addt(ans,mul(nend,fp(sum,M-2)));}cout<<ans;
}

转载于:https://www.cnblogs.com/Yuhuger/p/10668374.html

ZJOI2019 麻将相关推荐

  1. 【题解】Luogu P5279 [ZJOI2019]麻将

    原题传送门 希望这题不会让你对麻将的热爱消失殆尽 我们珂以统计每种牌出现的次数,不需要统计是第几张牌 判一副牌能不能和,类似这道题 对于这题: 设\(f[i][j][k][0/1]\)表示前\(i\) ...

  2. [ZJOI2019]麻将

    Luogu5279 , LOJ3042 题意:给出初始13张手牌,求理论可以和牌的最小轮数的期望.定义和牌为:4句话+1对乱将,不能有杠:七对 原始题解-shadowice 写得很好的题解 首先分析期 ...

  3. [ZJOI2019]麻将 题解(dp 套 dp)

    文章目录 前言 题面 题解 坑点 代码 前言 做这道题的想法从看到这场比赛的 T2 的题解时就开始了.3.1 ~ 3.16 号,共 16 天的历程,我才终于搞出来这道题.在这 16 天里,我每天都要花 ...

  4. ZJOI2019一轮停课刷题记录

    Preface 菜鸡HL终于狗来了他的省选停课,这次的时间很长,暂定停到一试结束,不过有机会二试的话还是可以搞到4月了 这段时间的学习就变得量大而且杂了,一般以刷薄弱的知识点和补一些新的奇怪技巧为主. ...

  5. 微软麻将AI Suphx或引入“凤凰房”,与其他AI对打

    作者 | 夕颜 出品 | AI科技大本营(ID:rgznai100) [导读]在刚刚结束的上海2019世界人工智能大会上,微软宣布了其在人工智能领域的最新研究突破--由微软亚洲研究院研发的麻将 AI ...

  6. 超越99.9%人类玩家,微软专业十段麻将AI论文细节首次公布

      视学算法分享   转自 | 机器之心 [导读]在去年 8 月底的世界人工智能大会上,时任微软全球执行副总裁的沈向洋正式对外宣布了微软亚洲研究院研发的麻将 AI「Suphx 」.近日,关于 Suph ...

  7. node.js——麻将算法(六)简易版麻将出牌AI1.0

    普通麻将的出牌AI如果不是要求特别高的话,其实蛮容易实现的,毕竟大多数人打牌都只是看自己的手牌. 所以作为简易版的AI,出牌的策略只要奔着胡牌去就可以了.我们能想到的就是把相邻或相同的牌凑到一起,把单 ...

  8. node.js——麻将算法(一)基本判胡

    首先带来的就是麻将胡牌.听牌的算法,不过大家都知道,麻将各个地方的规则都不同,所以相关算法也需要作出一定的调整. 先简单说一下本次demo的规则要求把. 1.不计番,也就是没那么多胡法,最后胡了就行. ...

  9. [ZJOI2019]线段树

    [ZJOI2019]线段树 https://www.luogu.org/blog/Sooke/solution-p5280 f不够,加上g g的转移?要特别注意没有直接访问的点.分5类点. 大力分类讨 ...

最新文章

  1. 中国计算机学会论坛上5专家激辩:量子计算机10年内成熟?中美之间还有5-6年差距...
  2. ai包装插件_关于DIP异型插件机导入与相关来料标准研究
  3. Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
  4. SAP SMP的一些截图
  5. 计算机硬件结构控制信息,计算机硬件的基本结构
  6. java vector 输出_5.7(java学习笔记)Vector、Enumeration
  7. hdu-2717(基础搜索bfs)
  8. c# 封装“度分秒”与弧度之间的转换 以及datagridview控件的应用
  9. 武道之路-炼体期五重天
  10. Mybatis-学习笔记(10)调用存储过程、存储函数
  11. C语言自学之路六(循环语句详解)
  12. vue示例todos
  13. 苹果12可以双开微信吗 iPhone 12双开微信教程「iphone技巧」
  14. C# 短消息提示 窗口位置
  15. app三种工具的元素定位与swipe 滑动
  16. 使用阿里云建站 ——记录踩过的坑
  17. Java获取指定年月的开始时间和结束时间
  18. 异次元个人发卡系统_开源发卡系统
  19. android x86主动防御,LBE安全大师(主动式防御软件) for Android v6.1.2235 官网版 中文官方安装版...
  20. Java码农进阶之路~流程控制-循环结构whileforbreak与continue

热门文章

  1. 目标检测的国内外研究现状
  2. ZooKeeper 命令操作
  3. 【unix】unix环境高级编程
  4. 别了,亲爱的读者朋友们!!让我们别处再相逢!
  5. MySQL的下载(最新版本)(一)
  6. 《脉脉:人才流动与迁徙2022》,遭”哄抢”的复合型程序员成IT黑马
  7. 计算机报名初始密码,计算机报名密码忘了怎嘛办
  8. java数组列表_java – 如何显示数组列表中的所有元素?
  9. ORA-32002 ORA-32017 ORA-32019
  10. 免费下载英文文献的网站