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 5379 Mahjong tree(树的遍历&组合数学)