由于是暴搜题,所以这篇博客只讲怎么优化剪枝,以及一些细节。
模拟消除思路:因为消除可以拆分成小的横条或竖条,而这些条的长度至少为三,所以一块可消除的区域至少会有一个中心点。这里的中心点可以不在正中间,只需要不是条上的第一个或者最后一个。
于是枚举中间点,搜索它为中心最多向四个方向能扩展多远。如果搜索出来的横向满足长度要求,或竖向满足长度要求,就给他们打上一个标记。
注意,这里只是打上标记,不能直接清零,很可能另一个方块的结算还得用到这个方块。
等到枚举所有的中间点并给所有可消除的方块打上标记之后,可以把所有标记上的方块清空。然后检查有没有地方可以落下去。
注意所有方块落下去之后还有可能接着消消乐,所以在落完之后还要再循环回去检查有没有可消除的方块。这里使用一个递归的代码来实现。
int count(int x){ bool vis[8][6],flag=0; int cnt=0; memset(vis,0,sizeof(vis)); for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j){ int col=Map[x][i][j]; if(!col) continue; int u=i,d=i,l=j,r=j; while(u>1&&Map[x][u-1][j]==col) u--; while(d<n&&Map[x][d+1][j]==col) d++; while(l>1&&Map[x][i][l-1]==col) l--; while(r<m&&Map[x][i][r+1]==col) r++; if(d-u>=2) for(int k=u;k<=d;++k) vis[k][j]=1; if(r-l>=2) for(int k=l;k<=r;++k) vis[i][k]=1; } for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j) if(vis[i][j]){ Map[x][i][j]=0; cnt++; } for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j) if(!Map[x][i][j]){ int s=i; while(s<n&&!Map[x][s][j]) s++; if(!Map[x][s][j]) continue; flag=1; Map[x][i][j]=Map[x][s][j]; Map[x][s][j]=0; } if(flag) cnt+=count(x); return cnt; }
注意最后倒数第三行。flag表示的是有没有方块落下,因为有落下方块就有新一轮消除的可能性,所以可以递归这个函数,直到没有任何方块落下为止。此时递归终止,开始回溯计算答案。
再说说剪枝的几个小技巧。
1、优先考虑坐标字典序小的向右移动,这样一旦搜到答案就是字典序最小的解。
2、只有当左边没有方块的时候才向左移动,否则右面方块向左移动等价于左面方块向右移动,而这个状态已经搜过了。
3、不交换两个颜色相同的方块。这个没什么好说的。
最后给出代码。
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cctype> #include<cstring> using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } int num; int n=7,m=5; int Max; int Map[10][10][10]; int posx[100],posy[100],move[100]; inline void copy(int x){ for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j) Map[x+1][i][j]=Map[x][i][j]; } int count(int x){ bool vis[8][6],flag=0; int cnt=0; memset(vis,0,sizeof(vis)); for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j){ int col=Map[x][i][j]; if(!col) continue; int u=i,d=i,l=j,r=j; while(u>1&&Map[x][u-1][j]==col) u--; while(d<n&&Map[x][d+1][j]==col) d++; while(l>1&&Map[x][i][l-1]==col) l--; while(r<m&&Map[x][i][r+1]==col) r++; if(d-u>=2) for(int k=u;k<=d;++k) vis[k][j]=1; if(r-l>=2) for(int k=l;k<=r;++k) vis[i][k]=1; } for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j) if(vis[i][j]){ Map[x][i][j]=0; cnt++; } for(register int i=1;i<=n;++i) for(register int j=1;j<=m;++j) if(!Map[x][i][j]){ int s=i; while(s<n&&!Map[x][s][j]) s++; if(!Map[x][s][j]) continue; flag=1; Map[x][i][j]=Map[x][s][j]; Map[x][s][j]=0; } if(flag) cnt+=count(x); return cnt; } void dfs(int use,int deep){ if(use!=0&&deep==Max) return; if(use==0){ if(deep==Max){ for(int i=1;i<=Max;++i) printf("%d %d %d ",posy[i]-1,posx[i]-1,move[i]); exit(0); } return; } copy(deep); for(register int j=1;j<=m;++j) for(register int i=1;i<=n;++i){ if(!Map[deep][i][j]) continue; if(Map[deep][i][j]!=Map[deep][i][j+1]&&j<m){ int a=Map[deep][i][j],b=Map[deep][i][j+1]; Map[deep+1][i][j]=b;Map[deep+1][i][j+1]=a; posx[deep+1]=i; posy[deep+1]=j; move[deep+1]=1; int q=count(deep+1); dfs(use-q,deep+1); copy(deep); } if(!Map[deep][i][j-1]&&j>1){ int a=Map[deep][i][j],b=Map[deep][i][j-1]; Map[deep+1][i][j]=b;Map[deep+1][i][j-1]=a; posx[deep+1]=i; posy[deep+1]=j; move[deep+1]=-1; int s=i; while(s>1&&Map[deep+1][s-1][j-1]==0){ Map[deep+1][s--][j-1]=0; Map[deep+1][s][j-1]=a; } int q=count(deep+1); dfs(use-q,deep+1); copy(deep); } } } int main(){ Max=read(); int start=0; for(int i=1;i<=m;++i) for(int j=1;;j++){ Map[0][j][i]=read(); if(Map[0][j][i]==0) break; start++; } dfs(start,0); printf("-1"); return 0; }