连连看
Problem Description
“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。
Input
输入数据有多组。每组数据的第一行有两个正整数n,m(0<n<=1000,0<m<1000),分别表示棋盘的行数与列数。在接下来的n行中,每行有m个非负整数描述棋盘的方格分布。0表示这个位置没有棋子,正整数表示棋子的类型。接下来的一行是一个正整数q(0<q<50),表示下面有q次询问。在接下来的q行里,每行有四个正整数x1,y1,x2,y2,表示询问第x1行y1列的棋子与第x2行y2列的棋子能不能消去。n=0,m=0时,输入结束。
注意:询问之间无先后关系,都是针对当前状态的!
Output
每一组输入数据对应一行输出。如果能消去则输出"YES",不能则输出"NO"。
Sample Input
3 4
1 2 3 4
0 0 0 0
4 3 2 1
4
1 1 3 4
1 1 2 4
1 1 3 3
2 1 2 4
3 4
0 1 4 3
0 2 4 1
0 0 0 0
2
1 1 2 4
1 3 2 3
0 0
Sample Output
YES
NO
NO
NO
NO
网上好多人说,这道题的测试数据很水啊。。。
Problem Description
“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。
玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。
Input
输入数据有多组。每组数据的第一行有两个正整数n,m(0<n<=1000,0<m<1000),分别表示棋盘的行数与列数。在接下来的n行中,每行有m个非负整数描述棋盘的方格分布。0表示这个位置没有棋子,正整数表示棋子的类型。接下来的一行是一个正整数q(0<q<50),表示下面有q次询问。在接下来的q行里,每行有四个正整数x1,y1,x2,y2,表示询问第x1行y1列的棋子与第x2行y2列的棋子能不能消去。n=0,m=0时,输入结束。
注意:询问之间无先后关系,都是针对当前状态的!
Output
每一组输入数据对应一行输出。如果能消去则输出"YES",不能则输出"NO"。
Sample Input
3 4
1 2 3 4
0 0 0 0
4 3 2 1
4
1 1 3 4
1 1 2 4
1 1 3 3
2 1 2 4
3 4
0 1 4 3
0 2 4 1
0 0 0 0
2
1 1 2 4
1 3 2 3
0 0
Sample Output
YES
NO
NO
NO
NO
YES
唉。。一道DFS题目,也可以BFS解。这道题今天搞了一天,有些小悲催。。
刚开始不知道怎么想的,用递归总是超时,最后都出现了栈溢出的错误提示。。。
后来就一直WA。。。好崩溃的说。
最后决定用栈来解这道题目,但发现在判定是否走过的路的数组上出现了问题。
就是你走过的路可以置1,但是当你发现这条路走不通时,如何把走过的路置回0呢?
我没想出来,后来看到别人用队列做的,但他的判断数组设置成了3维的,
最后又加了一个来看 某一点4个方向是否都遍历到,遍历过的置1,这样就避免重复走同一条路的情况。
我最后还是决定用递归做了,恩。。AC后发现7000+MS,跟人家0MS 的比快哭了的说。。
后来,看了看别人的剪枝,放到自己代码上,结果骤减到62MS ( ⊙o⊙ )!!!!
搜索的精髓果然还是在剪枝上啊!!
这道题难点就在于如何判断转向上,我是设置的dis数组上右下左(0,1,2,3),可以发现 i对2取模,为0时判定是上下走,为1时判定左右走,这样根据之前的方向和现在的方向就可以知道是否转向了。
强大的剪枝就是:
当转向2次后,来判定当前的点和终点 是否在同一条线路,如果不在同一条线路就直接返回。
代码:
#include <stdio.h>
#include <string.h>
int map[1005][1005],vis[1005][1005];
int dis[4][2]={-1,0,0,1,1,0,0,-1};
int n,m,f_x,f_y;
bool ispos;
// dfs开始
void dfs(int x,int y,int direct,int turn)
{
// 如果转向大于2 或者 已经找到了 则直接返回
if(turn>2 || ispos==1) return;
// 如果到了目标,直接判定可以连接,因为之前转向大于2的都返回了
if(x==f_x && y==f_y)
{
ispos=true;
return;
}
// 强大的剪枝啊=。=
if(turn==2)
{
if(x!=f_x && y!=f_y) return;
else if(x==f_x)
if(direct!=1) return;
else if(y==f_y)
if(direct!=0) return;
}
int i,xx,yy;
// 四个方向的遍历
for(i=0;i<4;++i)
{
xx=x+dis[i][0];
yy=y+dis[i][1];
// 边界判定
if(xx<1 || yy<1 || xx>n || yy>m || vis[xx][yy]==1) continue;
// 如果下一个点是0或者终点
if(map[xx][yy]==0 || (xx==f_x && yy==f_y))
{
vis[xx][yy]=1;
//根据原来的方向来确定是否转向
if(direct==-1) dfs(xx,yy,i%2,turn);
else if(direct==0)
{
if(i%2==1) dfs(xx,yy,1,turn+1);
else dfs(xx,yy,0,turn);
}
else if(direct==1)
{
if(i%2==0) dfs(xx,yy,0,turn+1);
else dfs(xx,yy,1,turn);
}
vis[xx][yy]=0;
}
}
}
int main()
{
int i,j,q,s_x,s_y;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0 && m==0) break;
// 输入
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
scanf("%d",&map[i][j]);
scanf("%d",&q);
while(q--)
{
scanf("%d%d%d%d",&s_x,&s_y,&f_x,&f_y);
// 如果搜寻的两个点不相等或者搜寻的点是空点(即搜寻的点有0)
if(map[s_x][s_y]!=map[f_x][f_y] || map[s_x][s_y]==0 )
{
printf("NO\n");
continue;
}
// 如果搜寻两个点都为本身(去掉后增加了点时间)
if(s_x==f_x && s_y == f_y && map[s_x][s_y]!=0)
{
printf("NO\n");
continue;
}
// 初始化
memset(vis,0,sizeof(vis));
ispos=false;
dfs(s_x,s_y,-1,0);
// 判断输出
if(ispos) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
网上好多人说,这道题的测试数据很水啊。。。
还有WA的可能就是因为方向的问题。。。
提供一组别人想的测试数据:
5 4
0 0 0 0
0 2 2 0
1 0 2 0
2 0 0 0
2 2 0 1
1
3 1 5 4
5 4
2 2 0 1
2 0 0 0
1 0 2 0
0 2 2 0
0 0 0 0
1
1 4 3 1
5 4
1 0 2 2
0 0 0 2
0 2 0 1
0 2 2 0
0 0 0 0
1
1 1 3 4
来源于
Pluser的新浪博客
0 0 0 0
0 2 2 0
1 0 2 0
2 0 0 0
2 2 0 1
1
3 1 5 4
5 4
2 2 0 1
2 0 0 0
1 0 2 0
0 2 2 0
0 0 0 0
1
1 4 3 1
5 4
1 0 2 2
0 0 0 2
0 2 0 1
0 2 2 0
0 0 0 0
1
1 1 3 4