HDU-OJ-1175 连连看

本文详细解析了连连看游戏中的消去逻辑实现,包括如何通过DFS深度优先搜索算法来判断两个棋子是否可以消去,以及在搜索过程中如何控制转向次数不超过两次。同时,文章还提供了一个具体的C++代码示例,帮助读者更好地理解和实现这一过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

连连看

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)


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
 

Author
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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值