您的位置:首页 > 其它

HDU-OJ-1175 连连看

2014-03-11 19:53 225 查看

连连看

[align=center]Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
[/align]

[align=left]Problem Description[/align]
“连连看”相信很多人都玩过。没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子。如果某两个相同的棋子,可以通过一条线连起来(这条线不能经过其它棋子),而且线的转折次数不超过两次,那么这两个棋子就可以在棋盘上消去。不好意思,由于我以前没有玩过连连看,咨询了同学的意见,连线不能从外面绕过去的,但事实上这是错的。现在已经酿成大祸,就只能将错就错了,连线不能从外围绕过。

玩家鼠标先后点击两块棋子,试图将他们消去,然后游戏的后台判断这两个方格能不能消去。现在你的任务就是写这个后台程序。

 

[align=left]Input[/align]
输入数据有多组。每组数据的第一行有两个正整数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时,输入结束。

注意:询问之间无先后关系,都是针对当前状态的!

 

[align=left]Output[/align]
每一组输入数据对应一行输出。如果能消去则输出"YES",不能则输出"NO"。

 

[align=left]Sample Input[/align]

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

 

[align=left]Sample Output[/align]

YES
NO
NO
NO
NO
YES

 

[align=left]Author[/align]
lwg
——————————————————————帅气分割线——————————————————————
思路:这不是我麻麻最喜欢玩的游戏嘛?!没想到写出来它酱不容易。需要在大脑中多开辟一个维度。哦不,是两个维度……其实,都一样嘛!需要有一个想法:增加状态。用状态控制DFS。从起点开始遍历地图,直到找到了终点为止,在此期间,转向次数不得超过2。我们知道DFS是一条路走到黑的,那么一旦此路不通,需要回到上一个还有路没试过的点。什么时候一个点算是彻底不必再访问呢?答案是:1.到该点时已经转向一次,四个方向都不通 2.到该点时已经转向两次,四个方向都不通。vis[x][y][direction][turn]浮现在脑海里。此外,有很多补丁需要打,尤其针对“方向”这个状态量。

这就是附加状态的vis数组。

代码如下:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <memory.h>
#include <stdlib.h>
#define LIM nx >= 1 && nx <= n && ny >=1 && ny <= m
int a[1010][1010];
bool vis[1010][1010][4][2];
int n, m, q, flag;
int sx, sy, ex, ey;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void dfs(int x, int y, int dir, int t){
int d;
if(t > 2 || flag) return ;//转向次数超过2或者已经断定可以配对,回城
if(x == ex&&y == ey){//走到了终点
flag = 1;
return ;
}
vis[x][y][dir][t] = 1;//对该转向次数该访问方向下的该结点做访问标记
for(d = 0; d < 4; d++){
int nx = x+dx[d], ny = y+dy[d];
if(!vis[nx][ny][d][t+(d != dir)] && !a[nx][ny] && LIM)
dfs(nx, ny, d, t+(d != dir));//t+(d != dir) 是对转向与否的判断。方向与父方向不一致,转向加1
}
return ;
}
int main(){
int i, j, d;
while(scanf("%d%d", &n, &m) != EOF&&n||m){
for(i = 1; i <= n; i++)
for(j = 1; j <= m; j++)
scanf("%d", a[i]+j);
scanf("%d", &q);//询问次数
while(q--){
flag = 0;
scanf("%d%d%d%d", &sx, &sy, &ex, &ey);
if(a[sx][sy] != a[ex][ey])  puts("NO");//起点和终点不是同样的方块,不能配对
else if(!a[sx][sy] || !a[ex][ey])  puts("NO");//终点或者起点有一个设在了什么都没有的地方,不能配对
else{//下面在主函数内,对“方向”等等打出补丁
int u = a[ex][ey];//首先保存终点方块的样子
a[ex][ey] = 0;//接着将其置为0,不然无法访问终点
memset(vis, 0, sizeof(vis));
for(d = 0; d < 4; d++){//从起点开始,有四个方向需要尝试,仅仅走能走通的方向,若不如此做,初始方向无法确定!默认是0(上)的话,vis数组就会出错
int x1 = sx+dx[d], y1 = sy+dy[d];
if(a[x1][y1] == 0)  dfs(x1, y1, d, 0);
}
a[ex][ey] = u;//这个补丁很重要!不恢复终点的样子,下次在“起点或终点有一个为0”的特判会出错!
if(flag)  puts("YES");
else  puts("NO");
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: