P1312 Mayan游戏
说实话,这道题目其实我以前是做过一遍的,但是当时做的时候完全是看题解一步步打出来的qwq,毕竟大模拟放到谁身上都不想做,好了回到正题。这道题目考验的实际上是回溯法的第三个方面:如何将自己的思考转换成代码实现。
这道题目搜索框架以及约束条件都不难确定,唯一的剪枝也是非常好想的,就是当两个相邻的方块如果相等时就不去移动了。那么唯一的难点就是如何将消除以及移动这两个操作做好。
那么如何消除呢?其实可以把当前游戏界面的各个状态遍历一遍找到所需要消除的方块,具体是这样实现:
bool remove(){
int flag=0;
for(int i=1;i<=5;i++){
for(int j=1;j<=7;j++){
if(i-1>=1&&i+1<=5&&mp[i][j]==mp[i-1][j]&&mp[i+1][j]==mp[i][j]&&mp[i][j]!=0){
del[i][j]=del[i+1][j]=del[i-1][j]=1;flag=1;
}
if(j-1>=1&&j+1<=7&&mp[i][j]==mp[i][j-1]&&mp[i][j+1]==mp[i][j]&&mp[i][j]!=0){
del[i][j]=del[i][j+1]=del[i][j-1]=1;flag=1;
}
}
}
if(!flag)return false;
if(flag){
for(int i=1;i<=5;i++){
for(int j=1;j<=7;j++){
if(del[i][j]){
del[i][j]=0;
mp[i][j]=0;
}
}
}
}
return true;
}
如何更新呢?实际上就是对于每一列,考虑每一列的分布情况,从最底层开始遍历到满足它的下面是空的但是本身却有方块的情况,然后开始往下找到这个方块应该被放到的位置,就会有:
void update(){
for(int i=1;i<=5;i++){
for(int j=1;j<=7;j++){
if(j-1>=1&&mp[i][j-1]==0&&mp[i][j]){
int p=j-1;
while(mp[i][p]==0&&p>=1)p--;
p++;
swap(mp[i][j],mp[i][p]);
}
}
}
}
最后就是dfs的设计了
dfs的状态不难考虑,就是当前的图的状态,换句话说抽象图的每一个节点都是图的状态,那么我们可以考虑用一个数组来表示之前的状态,然后回溯完毕后,将这个数组复制回原来的图中就可以了
void dfs(int k){
if(check()){
for(int i=1;i<=n;i++){
for(int j=1;j<=3;j++){
cout<<ans[i][j]<<" ";
}
cout<<endl;
}
exit(0);
}
if(k>n)return;
for(int i=1;i<=5;i++){
for(int j=1;j<=7;j++){
last[k][i][j]=mp[i][j];
}
}
for(int i=1;i<=5;i++){
for(int j=1;j<=7;j++){
if(i+1<=5&&mp[i][j]&&mp[i+1][j]!=mp[i][j]){
ans[k][1]=i-1;ans[k][2]=j-1;ans[k][3]=1;
move(i,j,1);
dfs(k+1);
for(int i=1;i<=5;i++){
for(int j=1;j<=7;j++){
mp[i][j]=last[k][i][j];
}
}
ans[k][1]=-1;ans[k][2]=-1;ans[k][3]=-1;
}
if(i-1>=1&&mp[i][j]&&mp[i-1][j]!=mp[i][j]){
ans[k][1]=i-1;ans[k][2]=j-1;ans[k][3]=-1;
move(i,j,-1);
dfs(k+1);
for(int i=1;i<=5;i++){
for(int j=1;j<=7;j++){
mp[i][j]=last[k][i][j];
}
}
ans[k][1]=-1;ans[k][2]=-1;ans[k][3]=-1;
}
}
}
}
然后这道题目就被完美地解决了。
小结
这道题目的构建是遵循先框架后细节的程序设计环节,即我们大概设计好搜索的框架,然后慢慢往里面填充细节。事先想好如何实现然后逻辑清晰的展现出来