HDU - 4431 Mahjong (模拟+搜索+哈希+中途相遇)

题目链接

基本思路:最理想的方法是预处理处所有胡牌的状态的哈希值,然后对于每组输入,枚举每种新加入的牌,然后用哈希检验是否满足胡牌的条件。然而不幸的是,由于胡牌的状态数过多(4个眼+一对将),预处理的复杂度太高($O(34^5)$),因此需要想办法优化一下。

我们可以预处理出所有“加上一对将之后可以胡牌”的状态,这样预处理的复杂度就成了$O(34^4)$,在可接受的范围内了。在检验的时候,只需要枚举去掉哪一对将,就可以$O(1)$检验是否能胡牌了(有种中途相遇的感觉),另外两种特殊情况单独判断即可。

玄学优化方法:

1.在dfs和枚举检验的时候动态维护哈希值,而不是每次重复计算,这样可以节省很大一部分计算哈希值的时间。

2.dfs的时候,每一层的初始下标都不小于上一层,这样可以避免很多重复状态。

3.用哈希表代替set,可以大幅缩短存取哈希值的时间。

4.预处理处所有不少于2张的牌,这样就不用每次枚举的时候都从头开始找了。

综上,总复杂度约为$O(34^4+20000*34*7)$,应该接近极限了吧。

其实这道题也没这么复杂,直接枚举将暴力吃碰就行了,但为了锻炼自己搜索的玄学优化能力还是选择了扬长避短o( ̄▽ ̄)d

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef unsigned long long ll;
 4 const int N=13+5,inf=0x3f3f3f3f,M=19260817;
 5 const char* s="mspc";
 6 int id[300],a[4][N];
 7 ll p[4][N],pm[40],h;
 8 struct D {int x,y;};
 9 vector<D> vec,vv;
10 struct Hashset {
11     static const int N=4e5,M=1e6+3;
12     int hd[M],nxt[N],tot;
13     ll p[N];
14     void clear() {memset(hd,-1,sizeof hd),tot=0;}
15     void insert(ll x) {
16         int u=x%M;
17         for(int i=hd[u]; ~i; i=nxt[i])if(p[i]==x)return;
18         p[tot]=x,nxt[tot]=hd[u],hd[u]=tot++;
19     }
20     int count(ll x) {
21         int u=x%M;
22         for(int i=hd[u]; ~i; i=nxt[i])if(p[i]==x)return 1;
23         return 0;
24     }
25 } st;
26 void dfs(int dep,int x,int y) {
27     if(st.count(h))return;
28     if(dep==4) {st.insert(h); return;}
29     for(int i=x; i<=3; ++i)
30         for(int j=(i==x?y:1); j<=(i==3?7:9); ++j) {
31             if(a[i][j]<=1) {
32                 a[i][j]+=3,h+=3*p[i][j];
33                 dfs(dep+1,i,j);
34                 a[i][j]-=3,h-=3*p[i][j];
35             }
36             if(j<=7&&i!=3&&a[i][j]<=3&&a[i][j+1]<=3&&a[i][j+2]<=3) {
37                 a[i][j]++,a[i][j+1]++,a[i][j+2]++,h+=p[i][j]+p[i][j+1]+p[i][j+2];
38                 dfs(dep+1,i,j);
39                 a[i][j]--,a[i][j+1]--,a[i][j+2]--,h-=p[i][j]+p[i][j+1]+p[i][j+2];
40             }
41         }
42 }
43 bool Chii() {
44     for(int i=0; i<=3; ++i)
45         for(int j=1; j<=(i==3?7:9); ++j)if(a[i][j]&&a[i][j]!=2)return 0;
46     return 1;
47 }
48 bool Kokushi() {
49     for(int i=0; i<=3; ++i)
50         for(int j=1; j<=(i==3?7:9); ++j) {
51             if((i!=3&&(j==1||j==9))||(i==3)) {if(!a[i][j])return 0;}
52             else if(a[i][j])return 0;
53         }
54     return 1;
55 }
56 bool Hu() {
57     for(D t:vv)if(st.count(h-2*p[t.x][t.y]))return 1;
58     return 0;
59 }
60 bool ok() {return Chii()||Kokushi()||Hu();}
61 int main() {
62     st.clear();
63     id[‘m‘]=0,id[‘s‘]=1,id[‘p‘]=2,id[‘c‘]=3;
64     pm[0]=1;
65     for(int i=1; i<40; ++i)pm[i]=pm[i-1]*M;
66     for(int i=0,k=33; i<=3; ++i)
67         for(int j=1; j<=(i==3?7:9); ++j,--k)p[i][j]=pm[k];
68     dfs(0,0,1);
69     int T;
70     for(scanf("%d",&T); T--;) {
71         memset(a,0,sizeof a),h=0;
72         for(int i=0; i<13; ++i) {
73             int x;
74             char ch;
75             scanf("%d%c",&x,&ch);
76             a[id[ch]][x]++,h+=p[id[ch]][x];
77         }
78         vec.clear(),vv.clear();
79         for(int i=0; i<=3; ++i)
80             for(int j=1; j<=(i==3?7:9); ++j)if(a[i][j]>=2)vv.push_back({i,j});
81         for(int x=0; x<=3; ++x)
82             for(int y=1; y<=(x==3?7:9); ++y)if(a[x][y]<=3) {
83                     a[x][y]++,h+=p[x][y];
84                     if(a[x][y]==2)vv.push_back({x,y});
85                     if(ok())vec.push_back({x,y});
86                     if(a[x][y]==2)vv.pop_back();
87                     a[x][y]--,h-=p[x][y];
88                 }
89         if(vec.size()) {
90             printf("%d",vec.size());
91             for(D t:vec)printf(" %d%c",t.y,s[t.x]);
92             puts("");
93         } else puts("Nooten");
94     }
95     return 0;
96 }

还有一种极限优化的方法,因为不同花色的牌是可以独立考虑的,因此单独判断出每种花色的牌是否合法(全为3或者111),如果能胡的话,则必然有三种花色合法,一种花色不合法,其中不合法的一组必然有一对将,枚举这对将,然后判断剩下的牌是否合法即可。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 typedef unsigned long long ll;
  4 const int N=13+2,inf=0x3f3f3f3f,M=19260817;
  5 const char* s="mspc";
  6 int id[300],a[4][N],c[N];
  7 ll pm[40],h[4],hh;
  8 struct D {int x,y;};
  9 vector<D> vec;
 10 struct Hashset {
 11     static const int N=4e5,M=1e6+3;
 12     int hd[M],nxt[N],tot;
 13     ll p[N];
 14     void clear() {memset(hd,-1,sizeof hd),tot=0;}
 15     void insert(ll x) {
 16         int u=x%M;
 17         for(int i=hd[u]; ~i; i=nxt[i])if(p[i]==x)return;
 18         p[tot]=x,nxt[tot]=hd[u],hd[u]=tot++;
 19     }
 20     int count(ll x) {
 21         int u=x%M;
 22         for(int i=hd[u]; ~i; i=nxt[i])if(p[i]==x)return 1;
 23         return 0;
 24     }
 25 } st1,st2;
 26 void dfs1(int dep,int u) {
 27     st1.insert(hh);
 28     if(dep==4)return;
 29     for(int i=u; i<=9; ++i) {
 30         if(c[i]<=1) {
 31             c[i]+=3,hh+=3*pm[i];
 32             dfs1(dep+1,i);
 33             c[i]-=3,hh-=3*pm[i];
 34         }
 35         if(c[i]<=3&&c[i+1]<=3&&c[i+2]<=3) {
 36             c[i]++,c[i+1]++,c[i+2]++,hh+=pm[i]+pm[i+1]+pm[i+2];
 37             dfs1(dep+1,i);
 38             c[i]--,c[i+1]--,c[i+2]--,hh-=pm[i]+pm[i+1]+pm[i+2];
 39         }
 40     }
 41 }
 42 void dfs2(int dep,int u) {
 43     st2.insert(hh);
 44     if(dep==4)return;
 45     for(int i=u; i<=7; ++i)if(c[i]<=1) {
 46             c[i]+=3,hh+=3*pm[i];
 47             dfs2(dep+1,i);
 48             c[i]-=3,hh-=3*pm[i];
 49         }
 50 }
 51 bool Chii() {
 52     for(int i=0; i<=3; ++i)
 53         for(int j=1; j<=(i==3?7:9); ++j)if(a[i][j]&&a[i][j]!=2)return 0;
 54     return 1;
 55 }
 56 bool Kokushi() {
 57     for(int i=0; i<=3; ++i)
 58         for(int j=1; j<=(i==3?7:9); ++j) {
 59             if((i!=3&&(j==1||j==9))||(i==3)) {if(!a[i][j])return 0;}
 60             else if(a[i][j])return 0;
 61         }
 62     return 1;
 63 }
 64 bool Hu() {
 65     int x=-1;
 66     for(int i=0; i<3; ++i)if(!st1.count(h[i])) {
 67             if(~x)return 0;
 68             x=i;
 69         }
 70     if(!st2.count(h[3])) {
 71         if(~x)return 0;
 72         x=3;
 73     }
 74     if(x!=3) {for(int i=1; i<=9; ++i)if(a[x][i]>=2&&st1.count(h[x]-2*pm[i]))return 1;}
 75     else {for(int i=1; i<=7; ++i)if(a[x][i]>=2&&st2.count(h[3]-2*pm[i]))return 1;}
 76     return 0;
 77 }
 78 bool ok() {return Chii()||Kokushi()||Hu();}
 79 int main() {
 80     st1.clear(),st2.clear();
 81     pm[0]=1;
 82     for(int i=1; i<40; ++i)pm[i]=pm[i-1]*M;
 83     id[‘m‘]=0,id[‘s‘]=1,id[‘p‘]=2,id[‘c‘]=3;
 84     dfs1(0,1),dfs2(0,1);
 85     int T;
 86     for(scanf("%d",&T); T--;) {
 87         memset(a,0,sizeof a);
 88         memset(h,0,sizeof h);
 89         for(int i=0; i<13; ++i) {
 90             int x;
 91             char ch;
 92             scanf("%d%c",&x,&ch);
 93             a[id[ch]][x]++,h[id[ch]]+=pm[x];
 94         }
 95         vec.clear();
 96         for(int x=0; x<=3; ++x)
 97             for(int y=1; y<=(x==3?7:9); ++y)if(a[x][y]<=3) {
 98                     a[x][y]++,h[x]+=pm[y];
 99                     if(ok())vec.push_back({x,y});
100                     a[x][y]--,h[x]-=pm[y];
101                 }
102         if(vec.size()) {
103             printf("%d",vec.size());
104             for(D t:vec)printf(" %d%c",t.y,s[t.x]);
105             puts("");
106         } else puts("Nooten");
107     }
108     return 0;
109 }

原文地址:https://www.cnblogs.com/asdfsag/p/11366200.html

时间: 2024-07-30 03:20:32

HDU - 4431 Mahjong (模拟+搜索+哈希+中途相遇)的相关文章

HDU 4431 Mahjong (麻将、神坑模拟题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4431 题面: Mahjong Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4219    Accepted Submission(s): 842 Problem Description Japanese Mahjong is a fou

HDU 4431 Mahjong(神奇的麻将) 模拟

胡牌的情况 1.有1对相同的牌(对子),和4组meld(对不起我实在不知道怎么翻译了).meld就是3张相同的牌或者3张连续的牌(顺子).注意3张连续的c牌不能看做meld. 2.有7对不同的对子. 3.有1s,9s,1m,9m,1p,9p,和1c至7c这13种牌,并且其中有1种牌有2张. 做法是枚举1张牌加入牌组(第14张牌),然后判断当前牌组能不能胡. 对于第一种胡牌情况,先选出1对对子,在判断剩下的是不是4组meld. 对于第二.三种情况就直接判断了. 需要注意的是,每种牌最多只有4张,需

HDU 4431 Mahjong (DFS,暴力枚举,剪枝)

题意:给定 13 张麻将牌,问你是不是“听”牌,如果是输出“听”哪张. 析:这个题,很明显的暴力,就是在原来的基础上再放上一张牌,看看是不是能胡,想法很简单,也比较好实现,结果就是TLE,一直TLE,这不科学啊... 好不容易写出来的,竟然TLE...心痛.就是先确定一个将牌,然后再对刻子和顺子进行分析,其实是要剪枝的,就是在如果有1张或者两张牌,而你又不能构成刻子的时候,就要返回false,因为这就已经没解了. 这一个剪枝,就AC了. 代码如下: #pragma comment(linker,

HDU - 4431 Mahjong

中华文化真是博大精深 这两天的Dark模拟题做的我要吐了 题目大意:给你13张麻将,求再加上哪张可以胡 胡的要求有三个 1.一个对子 +  4组 3个相同的牌或者顺子.  只有m.s.p是可以构成顺子的.东西南北这样的牌没有顺子. 2.7个不同的对子. 3.1m,9m,1p,9p,1s,9s,1c,2c,3c,4c,5c,6c,7c.  这13种牌每种都有,而且仅有这13种牌.肯定是有一种2张.其他的1张. 具体思路:枚举加那张牌,然后按题意模拟,判断是否胡了即可 我貌似wa了一页的样子,原因是

hdu 5379 Mahjong tree(树形dp)

题目链接:hdu 5379 Mahjong tree 树形dp,每个节点最多有2个子节点为一棵节点数大于1的子树的根节点,而且要么后代的节点值都大于,要么都小于本身(所以tson不为0是,要乘2).对于K个单一节点的子节点,种类数即为全排K!.当一个节点没有兄弟节点时,以这个节点为根结点的子树,根可以选择最大或者最小. #pragma comment(linker, "/STACK:102400000,102400000") #include <cstdio> #inclu

HDU 4937 Lucky Number 搜索

题意: 给你一个数,求在多少种不同的进制下这个数每一位都是3.4.5.6中的一个. 思路: 搜索.枚举这个数在任意进制下的表示,判断是否合法.当数字只有3.4.5.6时,必定有无穷种. 因为数字太大,所以直接枚举必定会超时. 下面有两种剪枝的方法: 1. 先枚举最后一位的情况. 假设数字n在base进制下表示为 a[n]...a[0],即 n = a[0] + a[1]*base^1 + ... + a[n]*base^n. 则 n - a[0] = a[1]*base^1 + ... + a[

HDU 1078 记忆化搜索

FatMouse and Cheese Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 4575 Accepted Submission(s): 1829 Problem Description FatMouse has stored some cheese in a city. The city can be considered as a

hdu 4964 Emmet(模拟)

题目链接:hdu 4964 Emmet 题目大意: 给定语句,按照语法翻译并输出. 解题思路:用递归模拟文法分析,主要注意几点: 括号并且的情况:(fuck)(you) 括号嵌套的情况:((fuck.you)) 优先输出id,然后是class(题目中有说) 乘法的部分:fuck*2>you*3 (每次执行fuck时,you的地方同样被执行了3次) 其他跑出样例基本没问题,具体看代码. #include <cstdio> #include <cstring> #include

HDU 4891 简单模拟

The Great Pan Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1035    Accepted Submission(s): 355 Problem Description As a programming contest addict, Waybl is always happy to take part in vario