【HDU】1175 连连看(BFS + 剪枝)

简介: 【HDU】1175 连连看(BFS + 剪枝)

image.png



目录

1.题目描述

2.输入输出

3.解题思路

4.样例解析

5.代码实现



1.题目描述


image.png



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

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


输入数据有多组。每组数据的第一行有两个正整数n,m(0


在接下来的n行中,每行有m个非负整数描述棋盘的方格分布。


0表示这个位置没有棋子,正整数表示棋子的类型。


接下来的一行是一个正整数q(0


在接下来的q行里,每行有四个正整数x1,y1,x2,y2,表示询问第x1行y1列的棋子与第x2行y2列的棋子能不能消去。


n=0,m=0时,输入结束。



2.输入输出


image.png



输入样例:


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


输出样例:


YES


NO


NO


NO


NO


YES



3.解题思路



这道题不同于寻常的 BFS 问题,他的限制条件在于 “转向”


本质上来说,题目所求解的是从 x1,y1 到 x2,y2 是否存在一条合法路径


之前所遇到的 标记数组 通常情况下是经过了这个点后将其标记,以后不再从这点经过


但在这题里面这个标记数组记录的应该是 转弯次数 的最小值


如果在 (i, j)这一点目前的转弯次数 step <= turn[i][j] , 那么就 Push 到队列里


因为从起点到一个点的路径有很多条,如果这点已经经过了就不再考虑的话,可能会忽略掉从这个点经过的其他转弯次数更好的路径(有点贪心的味道)


所以我们 标记数组 只存 小于或者是等于目前最小的 转弯次数 的路线


另外一种思路,参考了大佬的文章:


BFS——1175连连看_timer_catch的博客-CSDN博客


我们可以这样来拓展:一次性将一条直线上可以拓展的点全部拓展


每次都往四个方向进行直线拓展,同时也相当于是总步数 == 2时的剪枝操作



4.样例解析



image.png



5.代码实现


方法一


首先是方向的判断:



image.png


使用 0 1 2 3 来表示 四个方向, 同时也是与下面的 dx[], dy[] 数组所表示的方向数组相对应


image.png


然后是每个结点的组成 (坐标 + 方向 + 目前的转向次数)


image.png


struct Node
{
    int x, y;
    int dir;
    int step;
};


正常的读入 操作


image.png



核心代码 : BFS 函数  

首先是进行最开始的判断

① 所连接的两个元素是否相等

② 所连接的两个元素是否都是 0


image.png



接下来创建 BFS 中常用的队列, 初始化起点的结点


image.png


image.png


image.png


当我们可以拓展点的时候,需要进行以下判断:


因为我们初始化起点的方向为-1,所以下一个点可以往任意方向拓展


当入队的点不是起点的时候,需要判断它和队头元素方向是否一致


最后再与记录地图上每个点的转向次数最小值的 turn[i][j] 进行比较,如果小于等于,则可以入队



方法二


image.png



创建一个结构体,记录坐标和当前的总步数



image.png


同样初始的判断操作


image.png


在这种方法中,每个点只会被遍历一遍,所以我们需要用一个st数组来记录当前这个点是否走过,因此还有一步 额外的初始化操作

然后将起点加入队头


image.png


利用check函数,一次性将一条线上能满足条件的点全部放入队列中

同时利用了初始化起点的步数为-1的操作



AC代码

方法一:

#include <iostream>
#include <queue>
#include <cstring>
#define PII pair<int, int> 
using namespace std;
const int N = 1010;
int n, m, T;
int a1, b1, a2, b2;
int g[N][N];    //记录地图
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
//记录每个点的方向
//0  1  2  3 
//上 下 左 右
int turn[N][N]; //记录转向的次数
struct Node
{
    int x, y;
    int dir;
    int step;
};
bool bfs()
{
    //两个元素不相等
    if(g[a1][b1] != g[a2][b2]) return false;
    if(g[a1][b1] == 0 && g[a2][b2] == 0) return false;
    memset(turn, 20, sizeof turn);
    queue<Node> q;
    q.push({a1, b1, -1, 0});
    turn[a1][b1] = 0;
    Node temp, s;
    s.x = a1, s.y = b1, s.dir = -1, s.step = 0;
    while(q.size())
    {
        temp = q.front();
        q.pop();
        if(temp.x == a2 && temp.y == b2 && temp.step <= 2) return true;
        for(int i = 0; i < 4; i ++ )
        {
            s = temp;
            s.x += dx[i], s.y += dy[i];
            if(s.x >= 1 && s.x <= n && s.y >= 1 && s.y <= m && (g[s.x][s.y] == 0 || (s.x == a2 && s.y == b2))) 
            {
                if(s.dir != -1)
                {
                    if(i != s.dir)
                    {
                        s.dir = i;
                        s.step ++ ;
                    }
                }
                else s.dir = i;
                if(s.step <= turn[s.x][s.y])
                {
                    turn[s.x][s.y] = s.step;
                    q.push(s);
                }
            }
        }
    }
    return false;
}
int main()
{
    while(scanf("%d %d", &n, &m) && (n != 0 && m != 0))
    {
        for(int i = 1; i <= n; i ++ )
            for(int j = 1; j <= m; j ++ )
                cin >> g[i][j];
        cin >> T;
        while(T -- )
        {
            cin >> a1 >> b1 >> a2 >> b2;
            if(bfs()) puts("YES");
            else puts("NO");
        }
    }
    return 0;
}


方法二:


#include <iostream>
#include <queue>
#include <cstring>
#define PII pair<int, int> 
using namespace std;
const int N = 1010;
int n, m, T;
int a1, b1, a2, b2;
int g[N][N];    //记录地图
bool st[N][N];
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1};
struct Node
{
    int x, y;
    int step;
};
bool check(int x, int y)
{
    if(x == a2 && y == b2) return true;
    if(x < 1 || x > n || y < 1 || y > m || g[x][y] || st[x][y]) return false;
    return true;
}
bool bfs()
{
    //两个元素不相等
    if(g[a1][b1] != g[a2][b2]) return false;
    if(g[a1][b1] == 0 || g[a2][b2] == 0) return false;
    memset(st, false, sizeof st);
    queue<Node> q;
    q.push({a1, b1, -1});
    st[a1][b1] = true;
    while(q.size())
    {
        auto temp = q.front();
        q.pop();
        if(temp.x == a2 && temp.y == b2) return true;
        if(temp.step == 2) continue;
        for(int i = 0; i < 4; i ++ )
        {
           int a = temp.x + dx[i], b = temp.y + dy[i];
           while(check(a, b))
           {
               q.push({a, b, temp.step + 1});
               st[a][b] = true;
               a += dx[i], b += dy[i];
           }
        }
    }
    return false;
}
int main()
{
    while(scanf("%d %d", &n, &m) && (n != 0 && m != 0))
    {
        for(int i = 1; i <= n; i ++ )
            for(int j = 1; j <= m; j ++ )
                cin >> g[i][j];
        cin >> T;
        while(T -- )
        {
            cin >> a1 >> b1 >> a2 >> b2;
            if(bfs()) puts("YES");
            else puts("NO");
        }
    }
    return 0;
}


目录
相关文章
|
7月前
|
算法
最短路之Floyd算法
最短路之Floyd算法
83 1
|
6月前
|
存储
【洛谷 P2437】蜜蜂路线 题解(递归+记忆化搜索+高精度)
蜜蜂路线问题:蜜蜂从蜂房$m$到$n$($m&lt;n$)按数字递增爬行。给定$m$和$n$,求路线数。示例:$m=1$,$n=14$,输出$377$。100%数据$1\leq m,n\leq1000$。使用斐波那契序列优化,高精度处理大数。代码实现斐波那契存储并动态规划求解。
99 0
|
7月前
|
算法 测试技术 C++
【广度优先搜索】【拓扑排序】【C++算法】913. 猫和老鼠
【广度优先搜索】【拓扑排序】【C++算法】913. 猫和老鼠
|
7月前
|
算法
leetcode-675:为高尔夫比赛砍树 (最短路径算法bfs,dijkstra,A*)
leetcode-675:为高尔夫比赛砍树 (最短路径算法bfs,dijkstra,A*)
60 0
|
算法
算法练习——(9)汉诺塔问题
一.传说: 二.数学问题: 三.递归算法:
168 0
|
算法 Java
【递归与回溯算法】汉诺塔与八皇后问题详解
文章目录 1 汉诺塔问题 1.1 汉诺塔问题概述 1.2 思路分析 1.3 代码实现(Java) 1.4 结果验证 2 八皇后问题 2.1 八皇后问题概述 2.2 思路分析 2.2.1 问题划分与分析 2.2.2 涉及到的数据结构分析 2.2.3 上下对角线与行列的关系 2.3 代码实现(Java) 2.4 结果验证
【递归与回溯算法】汉诺塔与八皇后问题详解
|
算法
【算法刷题】—7.9动态规划,游戏跳跃,分解质因数
✨今日算法三题 1.使用最小花费爬楼梯 2.跳跃游戏 3.只有两个键的键盘
【算法刷题】—7.9动态规划,游戏跳跃,分解质因数
|
算法 Go
第十一章 运用广度优先搜索走迷宫
广度优先搜索类似于树的层次遍历。从图中的某一顶点出发,遍历每一个顶点时,依次遍历其所有的邻接点,然后再从这些邻接点出发,同样依次访问它们的邻接点。按照此过程,直到图中所有被访问过的顶点的邻接点都被访问到。
150 0
第十一章 运用广度优先搜索走迷宫
|
机器学习/深度学习 算法
【刷穿 LeetCode】求「连通图经过所有点的最短路径」的三种方式 :「BFS」&「Floyd + 状压 DP」 &「AStar 算法」
【刷穿 LeetCode】求「连通图经过所有点的最短路径」的三种方式 :「BFS」&「Floyd + 状压 DP」 &「AStar 算法」