题解 P1312 【Mayan游戏】

· · 题解

本题模拟的成分远大于搜索。

由于步数已经确定,所以只要采用最简单的DFS即可求出结果。

当然,我们需要一些必要的剪枝和优化:

1.最优化剪枝:按照x,y的顺序遍历方块,保证第一个找到的可行方案一定是最优方案。

2.最优化剪枝:只有当左边是空的时候才左移,否则等价于左边的右移。

3.最优化剪枝:不移动同色方块。

4.可行性剪枝:有某种方块个数<=2直接退出,因为不可能消除。

5.程序上的优化:用2个队列处理事件,一个处理掉落,一个处理消除。

第5个优化是重点。观察可以发现每一次移动方块都一定只会影响移动前后的两个位置,而出现可消除的方块序列的位置也必然包含被影响的位置。这样就可以用队列处理方块而不必要每一次遍历一遍地图。队列的具体用法代码里有提到。

这样就可以跑的非常快了。理论上还可以加一个估价的优化,通过同色方块的连接情况判断至少还要走几步,或者是记录当前最高的方块高度来避免无用的枚举,但实际上以上的优化已经足够了。

时间复杂度:O(?)

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cctype>
#define INF 2000000000
using namespace std;
typedef long long ll;
int read(){
    int f=1,x=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-f;c=getchar();}
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return f*x; 
}
//交换2个方块后要处理影响
//1.掉落  2.消除 
//设置一个掉落队列和一个事件队列
//事件队列检查是否有可以消除的方块,有的话就把最上面一层得上一个加入掉落队列 
//在掉落队列里检查上方是否有方块,有的话将上方的方块下降后全部加入事件队列 
//两者需要交替进行。
int pz[10][5][7];
int n,ans[10][3],movement[10][3],cnt[11],flag=0;
int dque[10005][2],dr,df;//掉落队列 
int eque[10005][2],er,ef;//事件队列 
int visx[10],visy[10],vis[5][7];
void solve_clear(int cur){
    int dx,dy,col,len;
    for(int i=0;i<5;i++)visx[i]=0;
    for(int i=0;i<7;i++)visy[i]=0;
    memset(vis,0,sizeof(vis));
    while(er>ef){
        dx=eque[ef][0],dy=eque[ef++][1];
        if(!visx[dx]){//同一个x 
            visx[dx]=1;
            col=pz[cur][dx][0],len=1;
            for(int i=1;i<7;i++){
                if(pz[cur][dx][i]==col)
                    len++;
                else{
                    if(len>=3&&col){
                        for(int j=i-1;i-j<=len;j--)vis[dx][j]=1;
                    }
                    col=pz[cur][dx][i],len=1;
                }
            }
            if(len>=3&&col){
                for(int j=6;7-j<=len;j--)vis[dx][j]=1;
            }
        }
        if(!visy[dy]){
            visy[dy]=1;
            col=pz[cur][0][dy],len=1;
            for(int i=1;i<5;i++){
                if(pz[cur][i][dy]==col)
                    len++;
                else{
                    if(len>=3&&col){
                        for(int j=i-1;i-j<=len;j--)vis[j][dy]=1;
                    }
                    col=pz[cur][i][dy],len=1;
                }
            }
            if(len>=3&&col){
                for(int j=4;5-j<=len;j--)vis[j][dy]=1;
            }
        }
    }
    for(int i=0;i<5;i++)
        for(int j=0;j<7;j++)
            if(vis[i][j]){
                pz[cur][i][j]=0;
                if(j!=6)
                    dque[dr][0]=i,dque[dr++][1]=j+1;
            }    
}
void solve_drop(int cur){
    int dx,dy,des;
    while(dr>df){
        dx=dque[df][0],dy=dque[df++][1];
        for(des=dy-1;des>=0&&!pz[cur][dx][des];des--);
        des++;
        for(int i=dy;i<7;i++)
            if(pz[cur][dx][i])
                pz[cur][dx][des++]=pz[cur][dx][i],
                eque[er][0]=dx,eque[er++][1]=des-1;
        for(int i=des;i<7;i++)
            pz[cur][dx][i]=0;
    }
}
void dfs(int cur){
    if(flag)return ;
    if(cur==n){
        for(int i=0;i<5;i++)
            if(pz[cur][i][0])return ;
        memcpy(ans,movement,sizeof(ans));
        flag=1;
        return ;
    }
    for(int i=1;i<=10;i++)cnt[i]=0;
    for(int i=0;i<5;i++)
        for(int j=0;j<7;j++)
            cnt[pz[cur][i][j]]++;
    for(int i=1;i<=10;i++)
        if(cnt[i]&&cnt[i]<3)return ;
    for(int i=0;i<5;i++){
        for(int j=0;j<7;j++){
            if(!pz[cur][i][j])continue;
            if(i!=4&&pz[cur][i+1][j]!=pz[cur][i][j]){
                //向右移动
                memcpy(pz[cur+1],pz[cur],sizeof(pz[0]));
                swap(pz[cur+1][i+1][j],pz[cur+1][i][j]);
                dr=df=0,ef=er=0;
                dque[dr][0]=i,dque[dr++][1]=j,
                dque[dr][0]=i+1,dque[dr++][1]=j;
                while(dr>df)
                    solve_drop(cur+1),solve_clear(cur+1);
                movement[cur][0]=i,movement[cur][1]=j,movement[cur][2]=1;
                dfs(cur+1);
            }
            if(i&&!pz[cur][i-1][j]&&pz[cur][i-1][j]!=pz[cur][i][j]){//向左边 
                memcpy(pz[cur+1],pz[cur],sizeof(pz[0]));
                swap(pz[cur+1][i-1][j],pz[cur+1][i][j]);
                dr=df=0,ef=er=0;
                dque[dr][0]=i,dque[dr++][1]=j,
                dque[dr][0]=i-1,dque[dr++][1]=j;
                while(dr>df){
                    solve_drop(cur+1),solve_clear(cur+1);
                }
                movement[cur][0]=i,movement[cur][1]=j,movement[cur][2]=-1;
                dfs(cur+1);
            }
        }
    }
}
void init(){
    n=read();
    int t;
    for(int i=0;i<5;i++)
        for(int j=0;j<8;j++){
            t=read();
            if(!t)break;
            pz[0][i][j]=t;
        }
}
void solve(){
    dfs(0);
    if(!flag)printf("-1\n");
    else {
        for(int i=0;i<n;i++)
            printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]);
    }
}
int main(){
    init();
    solve();
    return 0;
}

宣传博客:地址