zoukankan      html  css  js  c++  java
  • [NOIp2011] Mayan游戏

    Description

    Mayan puzzle是最近流行起来的一个游戏。游戏界面是一个(7)行×(5)列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上。游戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下:

    1 、每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参见输入输出样例说明中的图(6)到图(7));如果目标位置上没有方块,那么被拖动的方块将从原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见下面图(1)和图(2));

    2 、任一时刻,如果在一横行或者竖列上有连续三个或者三个以上相同颜色的方块,则它们将立即被消除(参见图(1)到图(3))。

    注意:

    • 如果同时有多组方块满足消除条件,几组方块会同时被消除(例如下面图(4),三个颜色为(1)的方块和三个颜色为(2)的方块会同时被消除,最后剩下一个颜色为(2)的方块)。

    • 当出现行和列都满足消除条件且行列共享某个方块时,行和列上满足消除条件的所有方块会被同时消除(例如下面图(5)所示的情形,(5)个方块会同时被消除)。

    • 方块消除之后,消除位置之上的方块将掉落,掉落后可能会引起新的方块消除。注意:掉落的过程中将不会有方块的消除。

    上面图(1)到图(3)给出了在棋盘上移动一块方块之后棋盘的变化。棋盘的左下角方块的坐标为((0),(0)),将位于((3),(3))的方块向左移动之后,游戏界面从图(1)变成图(2)所示的状态,此时在一竖列上有连续三块颜色为(4)的方块,满足消除条件,消除连续(3)块颜色为(4)的方块后,上方的颜色为(3)的方块掉落,形成图(3)所示的局面。

    Input

    (6)行。

    第一行为一个正整数(n),表示要求游戏通关的步数。

    接下来的(5)行,描述(7)×(5)的游戏界面。每行若干个整数,每两个整数之间用一个空格隔开,每行以一个(0)结束,自下向上表示每竖列方块的颜色编号(颜色不多于(10)种,从(1)开始顺序编号,相同数字表示相同颜色)。

    输入数据保证初始棋盘中没有可以消除的方块。

    Output

    如果有解决方案,输出(n)行,每行包含(3)个整数(x),(y),(g),表示一次移动,每两个整数之间用一个空格隔开,其中((x,y))表示要移动的方块的坐标,(g)表示移动的方向,(1)表示向右移动,(−1)表示向左移动。注意:多组解时,按照(x)为第一关健字,(y)为第二关健字,(1)优先于(-1) ,给出一组字典序最小的解。游戏界面左下角的坐标为((0,0))

    如果没有解决方案,输出一行,包含一个整数(−1)

    Sample Input

    3
    1 0
    2 1 0
    2 3 4 0
    3 1 0
    2 4 3 4 0
    

    Sample Output

    2 1 1
    3 1 1
    3 0 1
    

    Hint

    按箭头方向的顺序分别为图(6)到图(11)

    样例输入的游戏局面如上面第一个图片所示,依次移动的三步是:((2,1))处的方格向右移动,((3,1))处的方格向右移动,((3,0))处的方格向右移动,最后可以将棋盘上所有方块消除。

    对于(30%)的数据,初始棋盘上的方块都在棋盘的最下面一行;

    对于(100%)的数据,(0<n≤5)

    (noip2011)提高组(day1)(3)

    题解

    无脑码农题,改错改了半天。最后不得不在(Luogu)下了如下数据才该对……

    数据

    数据一:

    Input
    
    2
    1 0
    1 0
    0
    1 0
    1 0
    
    Output
    
    -1
    

    数据二:

    Input
    
    5
    1 1 2 3 4 0
    3 3 1 2 3 5 4 0
    3 5 5 3 2 2 0
    5 5 3 5 2 4 2 0
    0
    
    Output
    
    0 4 1
    0 2 1
    1 3 1
    3 2 1
    3 0 1
    

    定义:

    • (mp[][])记录读入的图,(Step)记录需要的步数

    • (Res[][])记录每一步的状态,(Ans[][])记录最终答案

    • (flag)标记是否找到答案,(YH[])数组用于优化

    const int maxn=15,maxstep=10,maxh=10,maxl=10;
    int Step,mp[maxh][maxl],Res[maxstep][4];
    int Ans[maxstep][4],flag,YH[maxn];
    

    读入

    读入很简单,但是第二层循环不要打成

    for(int j=1;j<=7;++j)
    

    因为有些输入输入了(8)个数,最后一项为(0)

    void Read()
    {
    	scanf("%d",&Step);
    	int num;
    	for(int i=1;i<=5;++i)
    		for(int j=1;;++j)
    		{
    			scanf("%d",&num);
    			if(num) mp[i][j]=num;
    			else break;
    		}
    }
    

    下落

    这个很简单,自己动手模拟一下就可以了:

    void Change(int (*nw)[maxl])
    {
    	int cnt;
    	for(int i=1;i<=5;++i)
    	{
    		cnt=0;
    		for(int j=1;j<=7;++j)
    			if(nw[i][j])
    			{
    				if(cnt) nw[i][j-cnt]=nw[i][j],nw[i][j]=0;
    			}
    			else ++cnt;
    	}
    }
    

    清理

    这个坑点很多,比如如果有四个、五个、……个连续的颜色是一起消除,还有图(5)所示的情况……

    我用的是用一个数组记录原来的数组,再行与列分开清理。

    bool Clean(int (*nw)[maxl])
    {
    	int tmp[maxh][maxl];
    	for(int i=1;i<=5;++i)
    		for(int j=1;j<=7;++j) tmp[i][j]=nw[i][j];
    	int cnt; bool flag=0;
    	for(int i=1;i<=5;++i)
    	{
    		cnt=0;
    		for(int j=1;j<=7;++j)
    		{
    			if(tmp[i][j])
    			{
    				if(tmp[i][j]==tmp[i][j-1]) ++cnt;
    				else cnt=1;
    			}
    			else cnt=0;
    			if(cnt==3)
    			{
    				flag=1,
    				nw[i][j-2]=nw[i][j-1]=nw[i][j]=0;
    			}
    			else if(cnt>3) nw[i][j]=0;
    		}
    	}
    	for(int j=1;j<=7;++j)
    	{
    		cnt=0;
    		for(int i=1;i<=5;++i)
    		{
    			if(tmp[i][j])
    			{
    				if(tmp[i][j]==tmp[i-1][j]) ++cnt;
    				else cnt=1;
    			}
    			else cnt=0;
    			if(cnt==3)
    			{
    				flag=1,
    				nw[i-2][j]=nw[i-1][j]=nw[i][j]=0;
    			}
    			else if(cnt>3) nw[i][j]=0;
    		}
    	}
    	return flag;
    }
    

    深搜

    深搜部分码量大,我们一个一个看,首先我们看一个减枝:

    bool Cut(int (*nw)[maxl])
    {
    	for(int i=0;i<maxn;++i) YH[i]=0;
    	for(int i=1;i<=5;++i)
    		for(int j=1;j<=7;++j) ++YH[nw[i][j]];
    	for(int i=1;i<maxn;++i)
    		if((YH[i])&&(YH[i]<3)) return 1;
    	return 0;
    }
    

    因为如果一种方块小于(3)个的话,那么它绝对不会被消那么继续(DFS)下去肯定找不到解,当存在这种情况时,我们就返回真。

    下面这个函数返回的是它的移动情况,返回值的意义如下:

    • 若为(0),则该数既不能左移,也不能右移

    • 若为(1),则该数只能左移

    • 若为(2),则该数只能右移

    • 若为(3),则该数既能左移,也能右移

    设该数所处的位置为((x,y))所有的情况如下:

    • (x)(1),则它不能左移;若(x)(5),则它不能右移

    • 仅仅只有((x-1,y))的位置没数时,该数才能左移;因为若((x-1,y))的位置有数,则((x-1,y))的右移就等价于((x,y))的左移,但是前者的字典序更小

    • ((x+1,y))的位置有数时,该数不需要右移

    int Let_me_think_think(int x,int y,int nw[maxh][maxl])
    {
    	if(!nw[x][y]) return 0;
    	if(x==1)
    	{
    		if(nw[x][y]!=nw[x+1][y]) return 1;
    		return 0;
    	}
    	if(x==5)
    	{
    		if(nw[x-1][y]) return 0;
    		return 2;
    	}
    	if(nw[x-1][y])
    	{
    		if(nw[x][y]!=nw[x+1][y]) return 1;
    		return 0;
    	}
    	if(nw[x][y]!=nw[x+1][y]) return 3;
    	return 2;
    }
    

    下面是一个简单的交换函数,不必解释

    void Swap(int &a,int &b)
    {
    	int tmp;
    	tmp=a,a=b,b=tmp;
    }
    

    最后就是很简单很麻烦的(DFS)

    void Dfs(int res,int nw[maxh][maxl])
    {
    	if(flag) return;
    	if(res>Step)
    	{
    		Check(nw); return;
    	}
    	if(Cut(nw)) return;
    	int tmp[maxh][maxl],QAQ;
    	for(int i=1;i<=5;++i)
    		for(int j=1;j<=7;++j)
    		{
    			QAQ=Let_me_think_think(i,j,nw);
    			if(!QAQ) continue;
    			if(QAQ==1||QAQ==3)
    			{
    				for(int ii=1;ii<=5;++ii)
    					for(int jj=1;jj<=7;++jj) tmp[ii][jj]=nw[ii][jj];
    				Res[res][1]=i,Res[res][2]=j,Res[res][3]=1;
    				Swap(tmp[i][j],tmp[i+1][j]);
    				Change(tmp);
    				while(Clean(tmp)) Change(tmp);
    				Dfs(res+1,tmp);
    			}
    			if(QAQ==2||QAQ==3)
    			{
    				for(int ii=1;ii<=5;++ii)
    					for(int jj=1;jj<=7;++jj) tmp[ii][jj]=nw[ii][jj];
    				Res[res][1]=i,Res[res][2]=j,Res[res][3]=-1;
    				Swap(tmp[i][j],tmp[i-1][j]);
    				Change(tmp);
    				while(Clean(tmp)) Change(tmp);
    				Dfs(res+1,tmp);
    			}
    		}
    }
    

    整体代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    const int maxn=15,maxstep=10,maxh=10,maxl=10;
    int Step,mp[maxh][maxl],Res[maxstep][4];
    int Ans[maxstep][4],flag,YH[maxn];
    
    void Read()
    {
    	scanf("%d",&Step);
    	int num;
    	for(int i=1;i<=5;++i)
    		for(int j=1;;++j)
    		{
    			scanf("%d",&num);
    			if(num) mp[i][j]=num;
    			else break;
    		}
    }
    
    void Check(int nw[maxh][maxl])
    {
    	for(int i=1;i<=5;++i)
    		for(int j=1;j<=7;++j)
    			if(nw[i][j]) return;
    	for(int i=1;i<=Step;++i)
    		Ans[i][1]=Res[i][1],
    		Ans[i][2]=Res[i][2],
    		Ans[i][3]=Res[i][3];
    	flag=1;
    }
    
    int Let_me_think_think(int x,int y,int nw[maxh][maxl])
    {
    	if(!nw[x][y]) return 0;
    	if(x==1)
    	{
    		if(nw[x][y]!=nw[x+1][y]) return 1;
    		return 0;
    	}
    	if(x==5)
    	{
    		if(nw[x-1][y]) return 0;
    		return 2;
    	}
    	if(nw[x-1][y])
    	{
    		if(nw[x][y]!=nw[x+1][y]) return 1;
    		return 0;
    	}
    	if(nw[x][y]!=nw[x+1][y]) return 3;
    	return 2;
    }
    
    void Swap(int &a,int &b)
    {
    	int tmp;
    	tmp=a,a=b,b=tmp;
    }
    
    void Change(int (*nw)[maxl])
    {
    	int cnt;
    	for(int i=1;i<=5;++i)
    	{
    		cnt=0;
    		for(int j=1;j<=7;++j)
    			if(nw[i][j])
    			{
    				if(cnt) nw[i][j-cnt]=nw[i][j],nw[i][j]=0;
    			}
    			else ++cnt;
    	}
    }
    
    bool Clean(int (*nw)[maxl])
    {
    	int tmp[maxh][maxl];
    	for(int i=1;i<=5;++i)
    		for(int j=1;j<=7;++j) tmp[i][j]=nw[i][j];
    	int cnt; bool flag=0;
    	for(int i=1;i<=5;++i)
    	{
    		cnt=0;
    		for(int j=1;j<=7;++j)
    		{
    			if(tmp[i][j])
    			{
    				if(tmp[i][j]==tmp[i][j-1]) ++cnt;
    				else cnt=1;
    			}
    			else cnt=0;
    			if(cnt==3)
    			{
    				flag=1,
    				nw[i][j-2]=nw[i][j-1]=nw[i][j]=0;
    			}
    			else if(cnt>3) nw[i][j]=0;
    		}
    	}
    	for(int j=1;j<=7;++j)
    	{
    		cnt=0;
    		for(int i=1;i<=5;++i)
    		{
    			if(tmp[i][j])
    			{
    				if(tmp[i][j]==tmp[i-1][j]) ++cnt;
    				else cnt=1;
    			}
    			else cnt=0;
    			if(cnt==3)
    			{
    				flag=1,
    				nw[i-2][j]=nw[i-1][j]=nw[i][j]=0;
    			}
    			else if(cnt>3) nw[i][j]=0;
    		}
    	}
    	return flag;
    }
    
    bool Cut(int (*nw)[maxl])
    {
    	for(int i=0;i<maxn;++i) YH[i]=0;
    	for(int i=1;i<=5;++i)
    		for(int j=1;j<=7;++j) ++YH[nw[i][j]];
    	for(int i=1;i<maxn;++i)
    		if((YH[i])&&(YH[i]<3)) return 1;
    	return 0;
    }
    
    void Dfs(int res,int nw[maxh][maxl])
    {
    	if(flag) return;
    	if(res>Step)
    	{
    		Check(nw); return;
    	}
    	if(Cut(nw)) return;
    	int tmp[maxh][maxl],QAQ;
    	for(int i=1;i<=5;++i)
    		for(int j=1;j<=7;++j)
    		{
    			QAQ=Let_me_think_think(i,j,nw);
    			if(!QAQ) continue;
    			if(QAQ==1||QAQ==3)
    			{
    				for(int ii=1;ii<=5;++ii)
    					for(int jj=1;jj<=7;++jj) tmp[ii][jj]=nw[ii][jj];
    				Res[res][1]=i,Res[res][2]=j,Res[res][3]=1;
    				Swap(tmp[i][j],tmp[i+1][j]);
    				Change(tmp);
    				while(Clean(tmp)) Change(tmp);
    				Dfs(res+1,tmp);
    			}
    			if(QAQ==2||QAQ==3)
    			{
    				for(int ii=1;ii<=5;++ii)
    					for(int jj=1;jj<=7;++jj) tmp[ii][jj]=nw[ii][jj];
    				Res[res][1]=i,Res[res][2]=j,Res[res][3]=-1;
    				Swap(tmp[i][j],tmp[i-1][j]);
    				Change(tmp);
    				while(Clean(tmp)) Change(tmp);
    				Dfs(res+1,tmp);
    			}
    		}
    }
    
    void Print()
    {
    	if(!flag) {puts("-1");return;}
    	for(int i=1;i<=Step;++i)
    		printf("%d %d %d
    ",Ans[i][1]-1,Ans[i][2]-1,Ans[i][3]);
    }
    
    int main()
    {
    	Read();
    	Change(mp);
    	while(Clean(mp)) Change(mp);
    	Dfs(1,mp);
    	Print();
    	return 0;
    }
    

    本文作者:OItby @ https://www.cnblogs.com/hihocoder/

    未经允许,请勿转载。

  • 相关阅读:
    K8s 使用 nfs-client-provisioner
    MySQL IF CASE 例子
    Nginx 限速
    Python 元组操作
    Python if, while,for,continue,break,三目运算符
    Centos7 安装 pyenv
    MySQL 查看大事务
    Tomcat 修改日志路径及日志分割
    游戏攻略 美少女万华镜5
    自建远程桌面过程 vnc + frp
  • 原文地址:https://www.cnblogs.com/hihocoder/p/11405859.html
Copyright © 2011-2022 走看看