NOIP2011Mayan游戏

NOIP的搜索题真的太让人难受了,总是不知道怎么剪枝。

交换两个颜色相同的块没有意义,因为这浪费了一步,这题首先要保证步数最少,然后再考虑字典序。

题目优先度的排序为右移优先于左移。

一个块的左边是非空块时不左移,否则会和之前的块右移重复。

搜索时记录原始状态。

#include<bits/stdc++.h>
using namespace std;
int n,m[11][11],ans[11][5],lt[11][11][11],v[11][11];
bool remove(){
    int fl=0;
    for(int i=1;i<=5;++i) for(int j=1;j<=7;++j){
        if(i-1>=1&&i+1<=5&&m[i][j]==m[i-1][j]&&m[i][j]==m[i+1][j]&&m[i][j]) v[i-1][j]=1,v[i+1][j]=1,v[i][j]=1,fl=1;
        if(j-1>=1&&j+1<=7&&m[i][j]==m[i][j+1]&&m[i][j]==m[i][j-1]&&m[i][j]) v[i][j]=1,v[i][j+1]=1,v[i][j-1]=1,fl=1;
    }if(!fl) return 0;
    for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) if(v[i][j]) v[i][j]=m[i][j]=0;
    return 1;
}
bool check(){for(int i=1;i<=5;++i) if(m[i][1])return 0;return 1;}
void copy(int x){for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) lt[x][i][j]=m[i][j];}
void update(){
    for(int i=1;i<=5;++i){int wow=0;
        for(int j=1;j<=7;++j) if(!m[i][j]) ++wow;else{if(!wow) continue;m[i][j-wow]=m[i][j],m[i][j]=0;}
    }
}
void move(int i,int j,int x){swap(m[i][j],m[i+x][j]);update();while(remove()) update();}
void dfs(int x){
    if(check()){for(int i=1;i<=n;++i){for(int j=1;j<=3;++j) printf("%d ",ans[i][j]);if(i!=n) puts("");}exit(0);}
    if(x==n+1) return;copy(x);
    for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) if(m[i][j]){
        if(i+1<=5&&m[i][j]!=m[i+1][j]){
            move(i,j,1);ans[x][1]=i-1;ans[x][2]=j-1;ans[x][3]=1;dfs(x+1);
            for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) m[i][j]=lt[x][i][j];
            ans[x][1]=-1;ans[x][2]=-1;ans[x][3]=-1;
        }
        if(i-1>=1&&m[i-1][j]==0){
            move(i,j,-1);ans[x][1]=i-1;ans[x][2]=j-1;ans[x][3]=-1;dfs(x+1);
            for(int i=1;i<=5;++i) for(int j=1;j<=7;++j) m[i][j]=lt[x][i][j];
            ans[x][1]=-1;ans[x][2]=-1;ans[x][3]=-1;
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=5;++i) for(int j=1;j<=8;++j){int x;scanf("%d",&x);if(x==0)break;m[i][j]=x;}
    memset(ans,-1,sizeof(ans));dfs(1);return !printf("-1");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值