连连看

连连看

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 25619    Accepted Submission(s): 6356

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



题解:

1.两棋子可以消去的条件:是两枚棋子(不能是1枚);两棋子必须相同且不能为空(0);两棋子连线上不能有其他棋子,只能为空(0);路线转折次数不能超过两次。
2.使用广搜来解,但由于广搜搜到的是最短路径,而这道题要求的路线转折次数不超过两次,所以需要的一个标记数组Min是用来存储数组中能到达各点的最小转折次数的,需要出示化为一个足够大的值,这里用了memset(Min,1,sizeof(Min));。

注意:一般情况下用memset()函数时一般都是用于全部初始化为0或-1,因为它们二进制的每一位都一样,memset()函数初始化时按位初始化的。

3.正整数n,m(0<n<=1000,0<m<1000),数组的输入从(1,1)开始。
4.当走到某一点(x,y)时,如果在该点的转折次数不大于Min[x][y],就应当刷新,然后按这条路走下去。


代码:

#include<stdio.h>
#include<string.h>
#include<string>
#include<queue>
using namespace std;

const int MAX = 1010;

int n,m,q;
int x1,x2,y1,y2;
int Map[MAX][MAX];
int Min[MAX][MAX];  //标记每个节点的最小转向次数
int Move[4][2]={{1,0},{0,1},
                {-1,0},{0,-1}};
struct Node
{
    int x,y,dir,ture; //dir记录当前的移动方向,ture记录转向次数
};

//判断该节点是否越界
bool Check(Node &h)
{
    if (h.x<1||h.y<1||h.x>n||h.y>m)
        return false;
    return true;
}

bool bfs()
{
    queue<Node> q;
    Node h,hh;
    h.x = x1;
    h.y = y1;
    h.ture = 0;
    for (int i=0;i<4;i++)
    {
        h.dir = i;
        q.push(h);
    }
    while (!q.empty())
    {
        h = q.front();
        if (h.x == x2 && h.y == y2 && h.ture <= 2)
        {
            while (!q.empty())
                q.pop();
            return true;
        }
        for (int i = 0; i < 4; i++)
        {
            //当前朝向与下一点的朝向不一样,转向
            if (h.dir != i)
            {
                hh.dir = i;
                hh.ture = h.ture+1;
            }
            else
            {
                hh.dir = h.dir;
                hh.ture = h.ture;
            }
            //二次剪枝
            if (hh.ture>2)
                continue;
            hh.x = h.x+Move[i][0];
            hh.y = h.y+Move[i][1];
            //三次剪枝
            /*
            注解1:
            Map[hh.x][hh.y]即Map[hh.x][hh.y]==0
            入队元素只能为0元素或终点元素,
            因为路线只能经过0元素或终点元素。

            注解2:
            Min[hh.x][hh.y]>=hh.ture
            这里Min是保存该节点最小转向次数,
            因为到达终点的一定是转向最少的路线,
            这个地方一定要想清楚,你一定不会没
            必要的瞎转弯浪费转弯次数。
            另外,这里的">=",大于是为了更新最小
            转弯次数,等于是为了让合适的结点入队。
            所以这里一定要用">="
            */
            if (Check(hh)&&(!Map[hh.x][hh.y]||(hh.x==x2&&hh.y==y2))&&(Min[hh.x][hh.y]>=hh.ture))
            {
                q.push(hh);
                Min[hh.x][hh.y]=hh.ture;
            }
        }
        q.pop();
    }
    return false;
}

int main()
{
    while (scanf("%d%d",&n,&m)&&m+n)
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=m;j++)
                scanf("%d",&Map[i][j]);
        scanf("%d",&q);
        while (q--)
        {
            //初始化Min数组为一个很大的值,这里为16843009,够用
            memset(Min,1,sizeof(Min));
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            //初步剪枝
            if (Map[x1][y1]!=Map[x2][y2]||!Map[x1][y1]||!Map[x2][y2]||(x1==x2&&y1==y2))
            {
                printf("NO\n");
                continue;
            }
            if (bfs())
                printf("YES\n");
            else
                printf("NO\n");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值