连连看——网易游戏2016实习生招聘

题目

这里写图片描述

小江最喜欢玩的游戏”天下3”最近推出了连连看的小玩法。玩家可以将2个相同图案的牌子连接起来,连接线不多于3根线段(即最多拐2折),就可以成功将这对牌子消除。如示意图所示,红色,黄色和蓝色的消除都是合法的,因为它们分别需要2个,0个和1个折。而黑色的消除是不合法的,因为这对牌至少需要拐3个折才能连接起来。

但是小江并不满足于这个游戏规则,因为他觉得最多只能拐2折这个限制并不合理。好奇的小江想知道的是,给定一个连连看的状态以及某一个牌子,在K折以内可以到达的所有具有相同图案的牌子的数量是多少。


输入

每个输入数据包含多个测试点。

第一行为测试点的个数S <= 20。之后是S个测试点的数据。

每个测试点的第一行为1 <= N <= 200, 1 <= M <= 200,表示连连看的大小。接下来的N行,每一行有M个整数,表示连连看该行的状态,如果为0,则表示该格为空,否则代表一种图案的牌子。

然后是三个整数X <= N, Y <= M,0 <= K <= 10000,表示查询(X, Y)这个牌子在K折以内能消除的所有相同牌子总数。其中连连看左上角的格子坐标为(1, 1),右下角为(N, M)。保证查询的格子是有图案的。


输出

对于每个测试点,输出对应的能消除的所有牌子总数。

提示
样例第一个例子,第(1, 1), (2, 3)和(2, 5)为3个可以在3折内被消除的相同牌子。


样例

输入
3
4 5
1 0 1 0 2
0 0 1 3 1
3 3 1 5 9
6 1 4 8 7
1 3 3
4 5
1 0 1 0 2
0 0 1 3 1
3 3 1 5 9
6 1 4 8 7
1 3 1
2 2
1 10
2 3
1 1 10
输出
3
2
0

分析

这题使用广搜的思想来做。
我们使用如下的数据结构表示一个搜索状态

struct State {
    int x, y;      //坐标
    int num;       //已转折次数
    int cx, cy;    //方向
}

对当前的状态,我们往跟原方向不在一条线上的两个方向发散(初始时往4个方向发散),直到遇到不为0或者不合法的坐标为止。将为0且未访问过的坐标用一个状态保存起来并压入队列。将所有已访问的坐标标记一下。记录下在转折次数不超过K时遇到的目标点的个数即为答案。


代码

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

struct State {
    int x, y;
    int num;
    int cx, cy;
    State() {}
    State(int ox, int oy, int onum, int ocx, int ocy) {
        x = ox; y = oy;
        num = onum;
        cx = ocx; cy = ocy;
    }
};

const int CX[] = {1, 0, -1, 0};
const int CY[] = {0, 1, 0, -1};
int Map[202][202];
bool vis[202][202];
int T, N, M, X, Y, K;

//检查是否出界
bool valid(int x, int y)
{
    return x >=0 && x <= N + 1 && y >= 0 && y <= M + 1;
}

int main() {
    scanf("%d", &T);
    while (T--) {
        memset(Map, 0, sizeof(Map));
        memset(vis, false, sizeof(vis));
        scanf("%d%d", &N, &M);
        for (int i = 1; i <= N; ++i)
            for (int j = 1; j <= M; ++j)
                scanf("%d", &Map[i][j]);
        scanf("%d%d%d", &X, &Y, &K);
        int count = 0;
        vis[X][Y] = true;
        queue<State> que;

        for (int i = 0; i < 4; i++)
        {
            int nx = X, ny = Y;
            while (true)
            {
                nx += CX[i];
                ny += CY[i];
                if (!valid(nx, ny)) break;
                if (Map[nx][ny] == 0)
                {
                    if (!vis[nx][ny])
                        que.push(State(nx, ny, 0, CX[i], CY[i]));
                    vis[nx][ny] = true;
                    continue;
                }
                if (Map[nx][ny] == Map[X][Y] && !vis[nx][ny])
                {
                    count++;
                }
                vis[nx][ny] = true;
                break;
            }
        }
        while (!que.empty())
        {
            State cur = que.front();
            que.pop();

            if (cur.num == K) continue;

            for (int i = 0; i < 4; i++)
            {
                //同方向跳过
                if (CX[i] == cur.cx && CY[i] == cur.cy) continue;
                if (CX[i] + cur.cx == 0 && cur.cy + CY[i] == 0) continue;
                int nx = cur.x, ny = cur.y;
                while (true)
                {
                    nx += CX[i];
                    ny += CY[i];
                    if (!valid(nx, ny)) break;
                    if (Map[nx][ny] == 0)
                    {
                        if (!vis[nx][ny])
                            que.push(State(nx, ny, cur.num + 1, CX[i], CY[i]));
                        vis[nx][ny] = true;
                        continue;
                    }
                    if (Map[nx][ny] == Map[X][Y] && !vis[nx][ny])
                    {
                        count++;
                    }
                    vis[nx][ny] = true;
                    break;
                }
            }
        }
        printf("%d\n", count);
    }
    return 0;
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值