题面:https://www.luogu.org/problemnew/show/P1312
搜索无疑
剪枝:
1.交换的两个块颜色相同,跳过。
2.如果一个块的左边有块,那么这个块左移不优。
因为左边的这个块右移效果相同,但是字典序更优。
我的剪枝:
1.如果一个颜色的块>=1且<=2,return
2.如果一种颜色的相邻距离-1的和>剩下步数,return(因为,一次操作,最多一个块进行靠拢)
3.哈希表判重
代码:
// luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; const int P=16; const int mo=1e5; const int M=800000+5; int mp[10][8]; int n; bool kp; int up=0; struct ha{ int cnt; ll val[M],nxt[M]; int hd[mo]; void ins(ll x){ int pos=x%mo; val[++cnt]=x;nxt[cnt]=hd[pos];hd[pos]=cnt; } bool query(ll x){ int pos=x%mo; for(int i=hd[pos];i;i=nxt[i]){ if(val[i]==x) return true; }return false; } }HA; void down(){ for(int j=1;j<=5;j++){ int top=1; for(int i=1;i<=7;i++){ if(mp[i][j]){ if(top!=i) mp[top][j]=mp[i][j],mp[i][j]=0; ++top; } } } } bool die[10][8]; bool kil(bool oo){ memset(die,0,sizeof die); for(int j=1;j<=5;j++){ int co=0; int cnt=0; int st=0; for(int i=1;i<=7;i++){ co=mp[i][j]; st=i; cnt=0; while(i<=7&&mp[i][j]==co) cnt++,i++; i--; if(cnt>=3&&co){ for(int k=st;k<=i;k++) die[k][j]=1; } } } for(int i=1;i<=7;i++){ int co=0; int cnt=0; int st=0; for(int j=1;j<=5;j++){ co=mp[i][j]; st=j; cnt=0; while(j<=5&&mp[i][j]==co) cnt++,j++; j--; if(cnt>=3&&co){ for(int k=st;k<=j;k++) die[i][k]=1; } } } bool over=false; for(int i=1;i<=7;i++){ for(int j=1;j<=5;j++){ if(die[i][j]){ over=true; mp[i][j]=0; } } } if(over) return true; return false; } struct node{ int x,y,d; }sta[10],ans[10]; int top; bool fl; int las[P]; int nd[P]; int num[P]; int che(int re){ memset(las,0,sizeof las); memset(nd,0,sizeof nd); memset(num,0,sizeof num); ll hsh=re; int sz=0; for(int j=1;j<=5;j++){ for(int i=1;i<=7;i++){ hsh=(hsh*P+mp[i][j])%mod; if(mp[i][j]==0) break; sz++; num[mp[i][j]]++; if(las[mp[i][j]]){ nd[mp[i][j]]+=j-las[mp[i][j]]-1; } las[mp[i][j]]=j; } } if(sz==0) return 2; if(HA.query(hsh)) return 0; for(int c=1;c<=up;c++){ if(nd[c]>re) return 0; if(num[c]==1||num[c]==2) return 0; } HA.ins(hsh); return 1; } bool cmp(){ for(int i=1;i<=n;i++){ if(sta[i].y<ans[i].y) return true; if(sta[i].y>ans[i].y) return false; if(sta[i].x<ans[i].x) return true; if(sta[i].x>ans[i].x) return false; if(sta[i].d>ans[i].d) return true; if(sta[i].d<ans[i].d) return false; } return false; } int shit=0; void dfs(int now){ if(now==n+1){ if(che(0)==2){ if(!fl){ fl=true; for(int i=1;i<=n;i++){ ans[i]=sta[i]; } } else if(cmp()){ for(int i=1;i<=n;i++){ ans[i]=sta[i]; } } } return; } if(!che(n-now+1)) return; int tmp[10][8]; memcpy(tmp,mp,sizeof mp); for(int j=1;j<=5;j++){ for(int i=1;i<=7;i++){ if(mp[i][j]){ if(j!=1&&mp[i][j]!=mp[i][j-1]&&(!mp[i][j-1])){ swap(mp[i][j],mp[i][j-1]); sta[now].x=i,sta[now].y=j; sta[now].d=-1; while(1){ down(); if(!kil(kp)) break; } dfs(now+1); sta[now].x=sta[now].y=sta[now].d=0; memcpy(mp,tmp,sizeof tmp); } if(j!=5&&mp[i][j]!=mp[i][j+1]){ swap(mp[i][j],mp[i][j+1]); sta[now].x=i,sta[now].y=j; sta[now].d=1; while(1){ down(); if(!kil(kp)) break; } dfs(now+1); sta[now].x=sta[now].y=sta[now].d=0; memcpy(mp,tmp,sizeof tmp); } } else break; } } } int main(){ scanf("%d",&n); for(int j=1;j<=5;j++){ int lp=0; int t; while(1){ scanf("%d",&t); up=max(up,t); if(t==0) break; mp[++lp][j]=t; } } fl=false; dfs(1); if(!fl){ printf("-1");return 0; } for(int i=1;i<=n;i++){ printf("%d %d %d ",ans[i].y-1,ans[i].x-1,ans[i].d); } return 0; }
错误点:
1.消除的时候中途出锅
2.HA表,必须再加上剩余步数作为hash值,否则会把正解冲突。
3.关键字的比较!我用的是x,y翻过来的情况,所以,程序中,应该先比较y,再比较x。
总结:
搜索这种东西,细节就是极多,写的时候一定要头脑清醒,不可大意。
而且,过多的递归,很难调试。