题目链接: 连连看

题目大意: 给一张n*m大小的图,问查询的两个点k1与k2之间能不能消掉,消掉的条件是通过走没有东西的路径拐弯不超过两次到达目标点。(0表示没有东西,>1的物品表示相应的东西),并且不能够走外围。

解题思路: 从起点向终点搜索可行解,有一个重要的剪枝是:当无转弯的次数时,还没到达与终点相同的行或列时排除该条搜索分枝。

AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
using namespace std;

int image[1010][1010],n,m;
int vis[1010][1010];
int x1,y1,x2,y2,flag;
int _next[4][2] = {0,1,1,0,0,-1,-1,0}; //方向: right down left up

inline bool judge (int x, int y) {
	if(x < 1 || x > n || y < 1 || y > m) {
		return false;
	}
	return true;
}

void DFS (int x, int y, int dis, int change_size) {
	if(change_size > 2 || flag == 1) {
		return ;
	} //check1:可行与不可行
	if(x == x2 && y == y2) {
		flag = 1;
		return ;
	} //check2: 搜索树的叶
	if(image[x][y] != 0 && dis != -1) {
				return ;
	} //check3:非出发点与终点不能有值
	if(change_size == 2 && (x != x2 && y != y2)) {
		return ;
	} //check4: 无转弯次数并且还没到达与终点的相同行或列
	
	int now_x,now_y;
	for (int i = 0; i < 4; i++) {
		now_x = x + _next[i][0];
		now_y = y + _next[i][1];
		if(judge(now_x, now_y) && vis[now_x][now_y] == 0) {
			if(dis == -1 || i == dis) {
				vis[now_x][now_y] = 1;
				DFS(now_x, now_y, i, change_size);
				vis[now_x][now_y] = 0;
			}else {
				vis[now_x][now_y] = 1;
				DFS(now_x, now_y, i, change_size + 1);
				vis[now_x][now_y] = 0;
			}
			if(flag == 1) return ;
		}
	}
	return ;
}

int main() {
	int T,i,j;
	
	while(~scanf("%d%d", &n,&m) && (n || m)) {
		for (i = 1; i <= n; i++) {
			for (j = 1; j <= m; j++) {
				scanf("%d", &image[i][j]);
			}
		}
		//query:T
		scanf("%d",&T);
		while (T--) {
			scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
			if(judge(x1,y1) && judge(x2,y2) && image[x1][y1] == image[x2][y2] && image[x1][y1] != 0) {
				memset(vis,0,sizeof(vis));
				vis[x1][y1] = 1;
				flag = 0;
				DFS(x1, y1, -1, 0);
				if(flag == 1) {
					puts("YES");
				}else {
					puts("NO");
				}
			}else {
				puts("NO");
			}
		}
	}
	return 0;
}