Mooyo Mooyo
题面
由于手上(更确实的,蹄子上)有大把的空余时间,Farmer John的农场里的奶牛经常玩电子游戏消磨时光。她们最爱的游戏之一是基于一款流行的电子游戏Puyo Puyo的奶牛版;名称当然叫做Mooyo Mooyo。
Mooyo Mooyo是在一块又高又窄的棋盘上进行的游戏,高
N
N
N(
1
≤
N
≤
100
1 \leq N \leq 100
1≤N≤100)格,宽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
1≤K≤10N)。以下
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;
}