hdu 1175 连连看

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


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
 

题意: 连连看。  就是给你一个N*M 的棋盘。  然后,  有Q次询问,  问两个坐标是否能消去(坐标可以相同)。 规则: 连线只能在棋盘内, 转折 turn <= 2;

思路:深搜。
 先别考虑时间,  怎么判断是否转折了。
次移动都有四个选择,  上, 下, 左, 右, 如图, 我是约定  0 代表 左     1 代表  右,   2 代表 上, 3 代表 下
那么 如果棋子是走 1(右), 3(下), 0(左), 那么就会到达 黄格。  
转折了2次, 分别是 在 从 走1  变为  走3 的时候, 和  从 走3 变为  走0 的时候 发生了转折。
如果棋子是走 1 (右), 3( 下), 3(下), 1(右), 1(右) ,那么就会到达 灰格子。  
也是发生了两次转折。 分别是从  走1 变为 走3 的时候,和  从 走3 变为 走1 的时候。 而 两次走3 和  两次走1 都未发生转折。
从上面的走法来看,不难发现, 只要当前走的方向与上次走的方向不一样就是 转折了。
那么 左 ==  (0, -1)   右 == (0, 1) 上 == (-1, 0) 下 == (1, 0);

下面就考虑时间问题啦!  
剪枝: 首先 按照规则剪枝  坐标对应的值是0  或者 两坐标对应的值不相等 或者   两坐标相同的 或者  转折大于2的   就输出 (“no")。
    
然后是最关键的剪枝:  如果 turn == 2 但是 当前位置与 目标位置不在同一直线上就要退出。 因为至少需要一个转折才能让当前位置移动到与目标在同一直线。
if(turn == 2 && x != index_i && y != index_j) return;

如果 当前位置与目标位置在同一直线,但当前移动方向不能移动到 目标位置的。
#include <stdio.h>
#include <string.h>
#define max_size 1005
int num[max_size][max_size], visited[max_size][max_size], index_i, index_j, flag, n, m;
int a[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
void dfs(int x, int y, int turn, int index) {
    if(flag)
        return;
	//违背规则的
    if(turn > 2)
        return;
    if(turn == 2) {
		//不在同一直线
		if(x != index_i && y != index_j) 
			return;
		//在同一列,运动方向不是往目标移动
		if(x != index_i)
			if(x - index_i > 0 && index != 1 || x - index_i < 0 && index != 0)
				return;
		//在同一行,运动方向不是往目标移动
		if(y != index_j)
			if(y - index_j > 0 && index != 3 || y - index_j < 0 && index != 2)
				return;
	}
	//满足条件的
    if(turn <= 2 && x == index_i && y == index_j) {
        flag = 1;
        return;
    }
    for(int i = 0; i < 4; ++i) {
        int nx = x + a[i][0], ny = y + a[i][1];
        if(!visited[nx][ny] && (!num[nx][ny] || nx == index_i && ny == index_j)) {
            if(i != index && index != -1)
                turn++;
            visited[nx][ny] = 1;
            dfs(nx, ny, turn, i);
            visited[nx][ny] = 0;
            if(i != index && index != -1)
                turn--;
        }
    }
}
int main() {
    while(scanf("%d%d", &n, &m) && n || m) {
        for(int i = 0; i <= n + 1; ++i)
            for(int j = 0; j <= m + 1; ++j)
                visited[i][j] = -1;
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j) {
                scanf("%d", &num[i][j]);
                visited[i][j] = 0;
            }
        int Q; 
        scanf("%d", &Q);
        while(Q--) {
            int start_i, start_j;
            scanf("%d%d%d%d", &start_i, &start_j, &index_i, &index_j);
            flag = 0;
            if(start_i != index_i || start_j != index_j)
                if(num[start_i][start_j] == num[index_i][index_j] && num[start_i][start_j] > 0) {
                    visited[start_i][start_j] = 1;
                    dfs(start_i, start_j, 0, -1);
                    visited[start_i][start_j] = 0;
                }
            if(flag)
                printf("YES\n");
            else
                printf("NO\n");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值