题目描述
大家都玩过连连看吧!今天我们玩一个类似的游戏。在一个由10*10个小方格组成的矩形里有n(n<=10)对字符(它们是大写字符中的前n个)。矩形里有些位置是可以从上面走过,有些则不能。能走过的位置用’.’标识,不能的用’#’标识。如果2个相同字符是连通的(从一个字符能走到另一个字符,注意走的时候只能向上、下、左、右走。某个位置是有其他字符时,这个位置是不能走的),那么这对字符能够进行配对。如果将这对字符配对,这对字符将从这个矩形里消除,也就是说这2个字符所在的位置对于其他字符而言变成能走动了。
现在的问题是:请你决定这些字符的配对顺序(只有能配对才能进行配对),使得n对字符最后都配对成功。
输入
先给出一个正整数t(t<=10),表示有t组测试数据。
每组测试数据有10行组成,每行有10个字符。这些字符只能是’.’,’#’,或者是大写字符中的前n个。每组测试数据中不超过10对字符。
输出
如果能够使每组测试数据中的n对字符配对成功,输出配对的顺序。如果有多种配对成功的顺序,输出字典序最小的那组。
否则输出”My God!”。
样例输入
2
ABF…….
CE……..
D………
……….
……….
……….
……….
………D
……..EC
…….FBA
ABF…….
CE……..
D………
……….
……….
………#
……..#D
………#
……..EC
…….FBA
样例输出
DCABEF
My God!
思路:先按照字典序找出所有的字母,然后用BFS依次判断能否成对消除。 在这个题中,最容易想到的是用一个for循环查找字母字符,但是循环次数太多,担心超限(勉强卡过去)。 无论是一次找出所有的字母,还是找到一个就调用BFS判断能否消除,这两种思路的队列必须要用数组模拟!(可能有别的思路,也可能可以调用库函数,太渣,目前不会。。) 下面的做法用到了map,这个STL是默认为按字典序排序的(从小到大) 所以可以节约时间,但调用了库函数,会增加时间,这个时间相比循环查找单个字母字符的少得多,故最终用时比它少。 当然,觉得不保险的话,可以自己排下顺序
#include<cstdio>
#include<map>
#include<iostream>
#include<queue>
#include<iterator>
#include<cstring>
#include<string> ///string类型头文件
using namespace std;
const int N=10;
int dxy[4][2]= {1,0,0,1,-1,0,0,-1};
char a[N][N];///输入的字符数组
int visit[N][N];///标志数组
struct point //记录字符的坐标(x,y)
{
int x,y;
} kk,po,que[505];
int bx,by;
bool in(int x,int y) //判断坐标(x,y)是否合法
{
if(x>=0&&x<N&&y>=0&&y<N)
return true;
return false;
}
map<char,point>mp;
map<char,point>::iterator it; ///迭代器
bool bfs(point pp,char ch)///判断能否成对消除字母
{
memset(visit,false,sizeof(visit)); ///标志置零
int flag=0,front=1,rear=1; //front为队头,rear为队尾
que[front].x=bx;
que[front].y=by;
visit[bx][by]=1;
while(front<=rear&&!flag) ///当队列不为空并且flag为0(flag=1时,即可以消除该字母)
{
int i=que[front].x;
int j=que[front].y;
for(int k=0; k<4; k++)
{
int nx=i+dxy[k][0];
int ny=j+dxy[k][1];
if(in(nx,ny)&&(a[nx][ny]=='.'||a[nx][ny]==ch)&&!visit[nx][ny]) //字符是"."时,加入队尾,是ch时,就消除
{
visit[nx][ny]=1;
rear++;
que[rear].x=nx;
que[rear].y=ny;
if(a[nx][ny]==ch)
{
a[nx][ny]=a[bx][by]='.'; ///这里被坑了很久,把bx,by写成了i,j;过了样例,却WA了。。 没有自己造的数据给自己挖了一个坑
flag=1;
break;
}
}
}
front++;//删除队头
}
return flag?true:false;
}
int main()
{
int n;
//freopen("E:/in.txt","r",stdin);
scanf("%d",&n);
while(n--)
{
for(int i=0; i<N; i++)
{
scanf("%s",a[i]);
for(int j=0; j<N; j++)
{
po.x=i,po.y=j;
if(a[i][j]!='.'&&a[i][j]!='#')
mp[a[i][j]]=po;///mp[a[i][j]]是子母时对应的坐标(i,j)的结构体po
}
}
string ans="";///输出的字母序列
if(mp.empty())///若没有字母,就直接输出
{
printf("My God!\n");
continue;
}
while(!mp.empty())//有字母时
{
int flag=0;
for(it=mp.begin(); it!=mp.end(); it++)
{
point tp=it->second;
char tpch=it->first;
bx=tp.x,by=tp.y;
if(bfs(tp,tpch))///如果字母tpch能够消除
{
flag=1;
ans=ans+tpch;///加在ans后面
mp.erase(tpch);
break;
}
}
if(!flag)///如果不能全部消除
{
ans="My God!";
break;
}
}
char p[25];
strcpy(p,ans.c_str());//转换为char * 类型,以便用printf输出,若不这样处理,会时间超限
printf("%s\n",p);
mp.clear();///mp清空便于下次的处理
}
return 0;
}
这里用到了STL中的map, 这是一种容器,默认按键的字典序排序的,在这段代码中,键就是char,值就是point(结构体类型)
char *p;
strcpy(p,ans.c_str());
printf(“%s\n”,p);
这里是将string类型的ans转化为char* 类型用printf输出,比cout输出(流)用时少