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

Posted asdfsag

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 

 

以上是关于HDU - 4431 Mahjong (模拟+搜索+哈希+中途相遇)的主要内容,如果未能解决你的问题,请参考以下文章

HDU-4431 麻将

HDU 5379 Mahjong tree(dfs)

hdu 5379 Mahjong tree 树形DP入门

HDU 5379 Mahjong tree(树的遍历&amp;组合数学)

2015多校第7场 HDU 5379 Mahjong tree 构造,DFS

DP---Mahjong tree