题意分析:
(1)题目是模拟开心消消乐的游戏逻辑,当交换两个相邻的图片时,如果交换后的面板中有连续的三个以上的相同的图片(拍成行或列),那么这些排列成行或列的相同的图形将消失,上面的图形将下落填充中间的空白,下落后有可能再次出现可消除的情况,于是同以上步骤,一直到最后没有可消除为止,求这次交换操作一共可消去多少个图形。
(2)题目对游戏的原型进行了简化:即消去一部分图形之后,没有新的图形产生,下落完成后,空余的部分用空白替代,图形用字符代替。我们可以先从第一个非‘.’的字符出发,作如下操作:从这个字符开始左右扫描,找出这一行是否有连续的三个以上(包括本字符)字符和本字符相同,设这一可消行的左右边界为(b,d);如没有连续的三个及以上,则从这个字符上下开始扫描。如找到可消列,设列的边界为(a,c)
(3)注意:这里并非找到连续的三个及以上相同的字符就立即消除,而是要顺着这个可消行(列)依次向上下(左右)继续搜索,找出在这个行(列)上其他的字符为中心上下扩展搜索的可消除列(行)的字符,称为"枢轴",见下图:
(4)如图:找到可消区域的枢轴之后,沿着枢轴找到上下边界(a,c),左右边界(b,d),然后删除这个边界内的图形;图一是正确的消除,反之,如果一开始找到连续的三个及以上的图形就删除,就会导致图二的错误,会由于消除枢轴之后,右边一列下落之后缺少一个,不能消除。
(5)设枢轴为arr[x][y],按照(4)消除之后,就需要将在x行以上(a,c)列之间的列向下移动一个位置,y列中空格上面的字符集体向下移动(d-1-b)个位置
(6)消除并移动之后,就会出现新的面板状态,这时有可能新的面板状态中由于图形的移动会出现新的可消行(列),于是我们又需要从头开始检索,如果存在可消行(列),就执行以上的重复步骤;反之,就开始检索下一个位置。这就是典型的回溯
可能坑点:
(1)没有找到枢轴,只找到可消行列就开始消除
(2)一定要注意可消行列的边界,本算法中边界不包括a、c、b、d,很容易不注意就弄错边界
(3)回溯的条件是:这一轮没有找到可消的行列,反应在数字上就是连续行、列相同的图形个数分别小于3,注意一定要是分别小于3!!!这里很容易出错。
(4)消除的时候下落距离要弄清
#include <iostream>
#include <stdio.h>
using namespace std;
int N,M;
char arr[20][20];
int res=0;
void cal()
{
for(int i=0;i<N;i++)
{
for(int j=0;j<M;j++)
{
//遇到非空格字符的时候才开始判断
if(arr[i][j]!='.')
{
int x=i,y=j;//最终消除区域的中心
int a=i,b=j,c=i,d=j;
int sum=0;
int temp=0;
//先开始纵向搜索
while((--a)>=0&&arr[a][j]==arr[i][j])temp++;
while((++c)<N&&arr[c][j]==arr[i][j])temp++;
//若纵向搜索可找到消除的列
if(temp>=2)
{
sum+=temp;
temp=0;
int maxum=0;
//开始在这个纵向列上以每个字符为中心开始横向搜索
for(int k=a+1;k<=c;k++)
{
int cnt=0;
int b1=j,d1=j;
while((--b1)>=0&&arr[k][b1]==arr[i][j])cnt++;
while((++d1)<M&&arr[k][d1]==arr[i][j])cnt++;
//若横向搜索可找到消除的行
if(cnt>=2&&cnt>maxum)
{
maxum=cnt;
//更新中心点
x=k;
y=j;
//更新左右边界
b=b1;
d=d1;
}
}
sum+=maxum;
}
else//若纵向搜索找不到消除的列
{
temp=0;
a=i,b=j,c=i,d=j;
//开始重新横向搜索
while((--b)>=0&&arr[i][b]==arr[i][j])temp++;
while((++d)<M&&arr[i][d]==arr[i][j])temp++;
//若横向搜索可找到消除的行
if(temp>=2)
{
sum+=temp;
temp=0;
int maxum=0;
//开始在这个横向行上以每个字符为中心开始纵向搜索
for(int k=b+1;k<d;k++)
{
int cnt=0;
int a1=i,c1=i;
while((--a1)>=0&&arr[a1][k]==arr[i][j])cnt++;
while((++c1)<N&&arr[c1][k]==arr[i][j])cnt++;
//若纵向搜索可找到消除的列
if(cnt>=2&&cnt>maxum)
{
maxum=cnt;
//更新中心点
x=i;
y=k;
//更新上下边界
a=a1;
c=c1;
}
}
sum+=maxum;
}
}
//若符合消除的条件
if(sum>=2)
{
res+=sum;
sum=0;
res++;
//若纵向上有消除的列,则消除后合并
if(c-a>=3)
{
for(int e=a;e>=0;e--)arr[e+c-1-a][y]=arr[e][y];
for(int e=0;e<c-a-1;e++)arr[e][y]='.';
}
//若横向上有消除的行,则消除后合并
if(d-b>=4)
{
for(int e=b+1;e<d;e++)
{
for(int f=x-1;f>=0;f--)arr[f+1][e]=arr[f][e];
arr[0][e]='.';
}
}
//开始向下回溯
cal();
}
}
}
}
}
int main()
{
int S,x0,y0,x1,y1;
cin>>S;
while(S--)
{
cin>>N>>M;
for(int i=0;i<N;i++)
{
for(int j=0;j<M;j++)cin>>arr[i][j];
}
scanf("%d %d %d %d",&x0,&y0,&x1,&y1);
char temp=arr[x0][y0];
arr[x0][y0]=arr[x1][y1];
arr[x1][y1]=temp;
cal();
printf("%d\n",res);
res=0;
}
return 0;
}