连连看 HDU - 1175 BFS+路径方向

16 篇文章 0 订阅

连连看,不考虑外连,内部连线。好比迷宫搜索路径采用BFS解题。转弯次数,可以采用4个方向每次记录当前方向,如果和上次方向不同那么就是转弯了,也就是记录方向和转弯数。代码实现就是向struct node中添加方向dir和转弯数step
由于第一步的方向不算转弯,所以step初始值为-1
第一种方法,扩展点重复计算。由于起点到终点有多条路径,每个点的转弯次数会重复计算,在搜索时不能使用vis标记扩展点,这样会定死扩展点的转弯次数,而且不是最优,如果标记当前点,不标记扩展点,那么队列中会出现多个坐标相同的点但是step不同,因为他们属于不同路径的点。

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 1e3 + 10;
char maze[MAXN][MAXN];
int vis[MAXN][MAXN];
int n, m, sx, sy, gx, gy;
struct node
{
    int x, y, dir, step;
};
int dx[] = {1, 0, 0, -1};
int dy[] = {0, -1, 1, 0};
bool bfs(int x, int y)
{
    //初始化vis
    memset(vis, 0, sizeof(vis));
    queue<node> q;
    node tmp1, tmp2;
    tmp1.x = x;
    tmp1.y = y;
    tmp1.step = -1;//转完次数为-1,第一步方向不算转弯,step=0
    tmp1.dir = -1;//初始方向为-1
    vis[x][y] = 1;
    q.push(tmp1);
    while (!q.empty())
    {
        tmp2 = q.front();
        q.pop();
        //终点转弯步数<=2满足条件
        if (tmp2.x == gx && tmp2.y == gy && tmp2.step <= 2) {return true;}
        for (int i = 0; i < 4; ++i)
        {
            tmp1 = tmp2;
            tmp1.x += dx[i];
            tmp1.y += dy[i];
            tmp1.dir = i;
            if (tmp1.x < 0 || tmp1.x >= n || tmp1.y < 0 || tmp1.y >= m || vis[tmp1.x][tmp1.y]) continue;
            //下一个点为终点或者下一个点为0
            if (maze[tmp1.x][tmp1.y] == '0' || (tmp1.x == gx && tmp1.y == gy))
            {
                if (tmp1.dir != tmp2.dir)
                    tmp1.step++;
                if (tmp1.step > 2)
                    continue;
                vis[tmp2.x][tmp2.y] = 1;
                q.push(tmp1);
            }
        }
    }
    return false;
}
int main()
{
    int q;
    while (cin >> n >> m)
    {
        if (n == 0 && m == 0) break;

        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j < m; ++j)
            {
                cin >> maze[i][j];
            }
        }

        cin >> q;
        while (q--)
        {
            cin >> sx >> sy >> gx >> gy;
            sx--;
            sy--;
            gx--;
            gy--;
            //起、终点相同且不为0
            if (maze[sx][sy] != '0' && maze[sx][sy] == maze[gx][gy] && bfs(sx, sy)) cout << "YES\n";
            else cout << "NO\n";
        }
    }
    return 0;
}

既然知道了第一种方式的弊端,就是重复计算扩展点
那么每次我们只需要比较当前转弯次数小于等于上一次到达该点的转弯次数即可。

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int MAXN = 1e3 + 10;
char maze[MAXN][MAXN];
int vis[MAXN][MAXN];//记录转弯次数
int n, m, sx, sy, gx, gy;
struct node
{
    int x, y, dir, step;
};
int dx[] = {1, 0, 0, -1};
int dy[] = {0, -1, 1, 0};
bool bfs(int x, int y)
{
    //初始化vis
    memset(vis, 3, sizeof(vis));//初始每个点的转弯次数为3次
    queue<node> q;
    node tmp1, tmp2;
    tmp1.x = x;
    tmp1.y = y;
    tmp1.step = -1;//转完次数为-1,第一步方向不算转弯,step=0
    tmp1.dir = -1;//初始方向为-1
    vis[x][y] = 1;
    q.push(tmp1);
    while (!q.empty())
    {
        tmp2 = q.front();
        q.pop();
        //终点转弯步数<=2满足条件
        if (tmp2.x == gx && tmp2.y == gy && tmp2.step <= 2) return true;
        //else if (tmp2.step > 2) return false;
        for (int i = 0; i < 4; ++i)
        {
            tmp1.step = tmp2.step;
            tmp1.x = tmp2.x + dx[i];
            tmp1.y = tmp2.y + dy[i];
            tmp1.dir = i;
            if (tmp1.x < 0 || tmp1.x >= n || tmp1.y < 0 || tmp1.y >= m ) continue;
            //下一个点为终点或者下一个点为0
            if (maze[tmp1.x][tmp1.y] == '0' || tmp1.x == gx && tmp1.y == gy)
            {
                if (tmp1.dir != tmp2.dir)
                    tmp1.step++;
                if (tmp1.step > 2)
                    continue;
                if (tmp1.step > vis[tmp1.x][tmp1.y])//当前转弯次数小于等于上次转弯次数,添加该扩展点
                    continue;
                vis[tmp1.x][tmp1.y] = tmp1.step;
                q.push(tmp1);
            }
        }
    }
    return false;
}
int main()
{
    int q;
    while (cin >> n >> m)
    {
        if (n == 0 && m == 0) break;

        for (int i = 0; i < n; ++i)
        {
            for (int j = 0; j < m; ++j)
            {
                cin >> maze[i][j];
            }
        }

        cin >> q;
        while (q--)
        {
            cin >> sx >> sy >> gx >> gy;
            sx--;
            sy--;
            gx--;
            gy--;
            //起、终点相同且不为0
            if (maze[sx][sy] != '0' && maze[sx][sy] == maze[gx][gy] && bfs(sx, sy)) cout << "YES\n";
            else cout << "NO\n";
        }
    }
    return 0;
}

没有过多的加入扩展点,总是保证当前路径最优。两种方法很明显,第二种更快。
在这里插入图片描述

游戏规则是模仿网络上普通的连连看游戏,主要是鼠标两次点击的图片能否消去的问题。当前,前提是点击两张相同的图片,若点击的是同一张图片或者两张不同的图片,则不予处理。在两张想同图片所能连通的所有路径中,如果存在一条转弯点不多于两个的路径,就可以消去;如果没有,则不予处理。 该游戏由30张不同的图片组成,游戏开始将会出现30张随机组合的图片,在规则下点击两张相同的图片后图片将会消失。图片全部消完为游戏成功。游戏还将设置退出,再来一局的按钮,并实现该功能,方便用户进行操作。 该游戏将有如下内容: (1)游戏计分功能 当消去两个相同的图片后分数将增加100分。 (2)退出功能 该功能有一个“退出”按钮,当按下“退出”按钮后,将直接退出游戏。 (3)再来一局功能 该功能有一个“再来一局”的按钮,当按下“再来一局”按钮后,图片将会重新排列,重新开始游戏。 (4)游戏倒计时功能 在游戏界面的上方有一个倒计时的进度条,增加游戏的难度,激发玩家的挑战兴趣。 (5)用户登录注册功能 在进入游戏界面之前,将会出现用户登录界面,如果没有注册的玩家在按下“注册”按钮后将进入注册界面,玩家需要填写用户名,密码,性别等信息完成注册,再进入登录界面,输入用户名和密码按下“确定”后就将进入游戏界面,开始游戏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值