标签:def 两种 输入 include amp pac clu 选择 优化方法
基本思路:最理想的方法是预处理处所有胡牌的状态的哈希值,然后对于每组输入,枚举每种新加入的牌,然后用哈希检验是否满足胡牌的条件。然而不幸的是,由于胡牌的状态数过多(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 (模拟+搜索+哈希+中途相遇)
标签:def 两种 输入 include amp pac clu 选择 优化方法
原文地址:https://www.cnblogs.com/asdfsag/p/11366200.html