NOIp提高组2011 Mayan游戏————搜索+剪枝

题解:本题主要考察搜索+剪枝
简要题意:题目较长,请看原题
1.搜索:首先我们需要这几个操作:
(1). c h a n g e change change:更新游戏的状态,就是把该掉下去的掉下去。
(2). M o v e Move Move:消除,搜索行列中连续三个颜色一样的方块,但不能马上清掉,因为当出现行列共享方块且满足时,行和列上满足的方块会被消除。所以用lazy记录先。
(3). m o v e move move:移动,移动后先更新游戏的状态。再判断是否可以消除,消除后再继续判断。
(4). c h e c k check check:检查,检查是否已经消除完,只用check第一行即可。
接下来就开始 D F S DFS DFS,记得回溯。
2.剪枝:这才是难点,一开始我没有剪,T了4个点。首先,颜色相同的方块跳过。接下来,我们发现,若左面有方块,那么你会在搜 i − 1 i-1 i1列时将其右移,和你在 i i i列时左移是等效的,所以可以减掉。只有左边没有方块是才左移。
另外,我把棋盘顺时针旋转 90 ° 90° 90°,方便记录操作。
代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int map[99][99],ans[99][10],lazy[99][99];
int yuan[11][11][11];
int n,flag1;
void copy(int x)
{
	for(int i=1;i<=5;i++)
	for(int j=1;j<=7;j++)
	yuan[x][i][j]=map[i][j];
}
void change()
{
	for(int i=1;i<=5;i++)
	{
		int sum=0;
		for(int j=1;j<=7;j++)
	    if(map[i][j]==0)sum++;
	    else 
		{
		    if(!sum)continue;
            map[i][j-sum]=map[i][j];
			map[i][j]=0;	
		}
	}
}
int Move()
{
	int flag=0;
	for(int i=1;i<=5;i++)
	for(int j=1;j<=7;j++)
	{
		if(i-1>=1&&i+1<=5&&map[i][j]==map[i+1][j]&&map[i][j]==map[i-1][j]&&map[i][j]!=0)
		lazy[i][j]=lazy[i-1][j]=lazy[i+1][j]=1,flag=1;
		if(j-1>=1&&j+1<=7&&map[i][j]==map[i][j+1]&&map[i][j]==map[i][j-1]&&map[i][j]!=0)
		lazy[i][j]=lazy[i][j-1]=lazy[i][j+1]=1,flag=1;
    }
    if(flag==0)return 0;
    for(int i=1;i<=5;i++)
    for(int j=1;j<=7;j++)
    if(lazy[i][j]!=0)
	{
		map[i][j]=0;
		lazy[i][j]=0;
	}
	return 1;
}
void move(int i,int j,int x)
{
	int a=map[i][j];
	map[i][j]=map[i+x][j];
	map[i+x][j]=a;
	change();
	while(Move()==1)change();
}
bool check()
{
	for(int i=1;i<=5;i++)
	if(map[i][1])return 0;
	return 1;
}
void dfs(int x)
{
	if(check()==1)
	{
		for(int i=1;i<=n;i++)
		cout<<ans[i][1]<<" "<<ans[i][2]<<" "<<ans[i][3]<<endl;
		flag1=1;exit(0);
	}
	if(x==n+1)return;
	copy(x);
	for(int i=1;i<=5;i++)
	for(int j=1;j<=7;j++)
	{
		if(map[i][j]){
		if(i+1<=5&&map[i][j]!=map[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++)
			map[i][j]=yuan[x][i][j];
			ans[x][1]=-1;ans[x][2]=-1;ans[x][3]=-1;
		}
		if(i-1>=1&&map[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++)
			map[i][j]=yuan[x][i][j];
			ans[x][1]=-1;ans[x][2]=-1;ans[x][3]=-1;
		}
	    }
	}
}
int main()
{
	cin>>n;
    for(int i=1;i<=5;i++)
    for(int j=1;j<=8;j++)
	{
		int x;cin>>x;
        if(x==0)break;
        map[i][j]=x;
    }
    memset(ans,-1,sizeof(ans));
    dfs(1);
    if(flag1!=1)
    cout<<-1;
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值