USACO :Mooyo Mooyo

Mooyo Mooyo

题面

由于手上(更确实的,蹄子上)有大把的空余时间,Farmer John的农场里的奶牛经常玩电子游戏消磨时光。她们最爱的游戏之一是基于一款流行的电子游戏Puyo Puyo的奶牛版;名称当然叫做Mooyo Mooyo。
Mooyo Mooyo是在一块又高又窄的棋盘上进行的游戏,高 N N N 1 ≤ N ≤ 100 1 \leq N \leq 100 1N100)格,宽10格。 这是一个 N = 6 N = 6 N=6的棋盘的例子:
0000000000
0000000300
0054000300
1054502230
2211122220
1111111223
每个格子或者是空的(用0表示),或者是九种颜色之一的干草捆(用字符1…9表示)。重力会使得干草捆下落,所以没有干草捆的下方是0。
如果两个格子水平或垂直方向直接相邻,并且为同一种非0颜色,那么这两个格子就属于同一个连通区域。任意时刻出现至少 K K K个格子构成的连通区域,其中的干草捆就会全部消失,变为0。如果同时出现多个这样的连通区域,它们同时消失。随后,重力可能会导致干草捆向下落入某个变为0的格子。由此形成的新的布局中,又可能出现至少 K K K个格子构成的连通区域。若如此,它们同样也会消失(如果又有多个这样的区域,则同时消失),然后重力又会使得剩下的方块下落,这一过程持续进行,直到不存在大小至少为 K K K的连通区域为止。
给定一块Mooyo Mooyo棋盘的状态,输出这些过程发生之后最终的棋盘的图案。

输入格式

文件名:mooyomooyo.in
输入的第一行包含 N N N K K K 1 ≤ K ≤ 10 N 1 \leq K \leq 10N 1K10N)。以下 N N N行给出了棋盘的初始状态。

输出格式

文件名:mooyomooyo.out:
输出 N N N行,描述最终的棋盘状态。
####输入样例:

6 3
0000000000
0000000300
0054000300
1054502230
2211122220
1111111223

####输出样例:

0000000000
0000000000
0000000000
0000000000
1054000000
2254500000

在上面的例子中,如果K=3
,那么存在一个大小至少为K

的颜色1的连通区域,同样有一个颜色2的连通区域。当它们同时被移除之后,棋盘暂时成为了这样:

0000000000
0000000300
0054000300
1054500030
2200000000
0000000003

然后,由于重力效果,干草捆下落形成这样的布局:

0000000000
0000000000
0000000000
0000000000
1054000300
2254500333

再一次地,出现了一个大小至少为K

地连通区域(颜色3)。移除这个区域就得到了最终的棋盘布局:

0000000000
0000000000
0000000000
0000000000
1054000000
2254500000

题解

  • 大模拟…码农题…头很大啊…
    我们暂且叫它俄罗斯方块消消乐
  • 这道题分为3部:判断存在能消的方块→消掉方块→下落
    每一个都必须与dfs(flood-fiil)来模拟,因为方格的形状是不固定的。
  • 第一步:暴力求联通块,顺便求出和每一个联通块所在的联通块大小。有≥k的就继续,否则就结束循环。搞个死循环就好,让电脑慢慢搞
    第二部:消方块。根据第一步求出的数字,≥k的搞成0即可。
    第三部:下落。按照行数倒序枚举。最下那么先枚举到的一定是最底层的,叠在最小面并且从下往上叠。至于怎么叠?用一个数组标记即可。
  • 对于代码的解释:搜索的代码占了大部分,本质相同,因为具体操作不同所以分开来谢了。用函数写应该还是比较直观的了吧!
  • 代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,k;
int vd[1000];
int f[101][11];
int v[101][11];
int u[101][11];
int nm[101][11];
char a[101][11];
char temp[101][11];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,-1,1};
int num(int x,int y)
{
	int sum=1;
	v[x][y]=1;
	for (int i=0;i<4;++i)
	{
		int nx=x+dx[i],ny=y+dy[i];
		if (nx>=1 && nx<=n && ny>=1 && ny<=10)
		if (!v[nx][ny] && a[x][y]==a[nx][ny]) sum+=num(nx,ny);
	}
	return sum;
}
//求x,y所在连通块大消息 
void cover(int x,int y)
{
    u[x][y]=1;
    for (int i=0;i<4;++i)
    {
    	int nx=x+dx[i],ny=y+dy[i];
    	if (nx>=1 && nx<=n && ny>=1 && ny<=10)
    	if (!u[nx][ny] && a[x][y]==a[nx][ny])
    	{
    		nm[nx][ny]=nm[x][y];
    		cover(nx,ny);
		}
	}
	return;
}
//求出某一个节点所在连通块大小后不断覆盖,节约时间 
bool many(void)
{
	memset(v,0,sizeof(v));
	memset(u,0,sizeof(u));
	for (int i=1;i<=n;++i)
	    for (int j=1;j<=10;++j)
	        if (nm[i][j]==0) 
	        {
	        	nm[i][j]=num(i,j);
	        	cover(i,j);
			}
	for (int i=1;i<=n;++i)
	    for (int j=1;j<=10;++j)
	        if (nm[i][j]>=k && a[i][j]!='0') return true;
	return false;
}
//判断要不要继续消下去 
void die(int x,int y)
{
	v[x][y]=1;
	char now=a[x][y];
	a[x][y]='0';
    for (int i=0;i<4;++i)
    {
    	int nx=x+dx[i],ny=y+dy[i];
    	if (nx>=1 && nx<=n && ny>=1 && ny<=10)
    	if (!v[nx][ny] && now==a[nx][ny]) die(nx,ny);
	}
}
//消成0 
void noit(void)
{
	for (int i=1;i<=n;++i)
	    for (int j=1;j<=10;++j)
		    if (a[i][j]!='0' && nm[i][j]>=k) 
		        die(i,j);
	return;
}
//枚举消的坐标 
void downit(void)
{
	memset(temp,'0',sizeof(temp));
	for (int i=1;i<=100;++i) vd[i]=n+1;
	//掉落位置的预处理 
	for (int i=n;i;--i)
	    for (int j=1;j<11;++j)
		    if (a[i][j]>'0') vd[j]--,temp[vd[j]][j]=a[i][j];
	//temp用来暂时存储图像 
	for (int i=1;i<=n;++i)
	    for (int j=1;j<11;++j)
	        a[i][j]=temp[i][j];
	return;
} 
//掉落 
void print(void)
{
	for (int i=1;i<=n;++i)
	    for (int j=1;j<=10;++j)
	        if (j==10) 
	        {
	        	if (i==n) cout<<a[i][j];
	        	else cout<<a[i][j]<<'\n';
			}
	        else cout<<a[i][j];
    cout<<endl;
}
//打印结果 
int main(void)
{
	freopen("mooyomooyo.in","r",stdin);
	freopen("mooyomooyo.out","w",stdout);
	cin>>n>>k;
	for (int i=1;i<=n;++i) 
	    for (int j=1;j<=10;++j)
	        cin>>a[i][j];
	while (many()==true) 
	{
	    memset(v,0,sizeof(v));
	    memset(u,0,sizeof(u));
		noit();
		downit();
		memset(nm,0,sizeof(nm));
	}
	print();
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值