/*
看到这题的限时和空间~,马上不用纠结了,暴搜+剪枝必过
要是哪个大神能告诉我更好的剪枝步骤,我会很感激的。因为
我的代码简直是蜗牛速度~456ms 4900KB水过
*/
#include <iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
#include<memory.h>
#include<algorithm>
using namespace std;
#define inf 0x3ffffff
#define M 1005
int map[M][M],wan[M][M]; //map是图;wan是弯的数量,主要用来剪枝
int go[4][2]= {{-1,0},{1,0},{0,-1},{0,1}}; //go表示走上、下、左、右方向
int query,s_x,s_y,e_x,e_y,R,C; //query表示问题的个数,s_x,s_y表示第一个棋子的位置,
bool F;
struct node
{
int x,y,turn,now;
node() {}
node(int a,int b,int c,int d) //构造函数万岁~turn代表弯的个数,now表示当前方向
{
x=a;
y=b;
turn=c;
now=d;
}
};
void bfs()
{
for(int i=1; i<=R; ++i)
for(int j=1; j<=C; ++j)
wan[i][j]=inf;
queue<node> Q;
node f(s_x,s_y,-1,6); //f.turn赋值-1,省事~f.now随便一个数都可以只要不是0,1,2,3
Q.push(f);
while(!Q.empty()&&!F)
{
node s=Q.front();
Q.pop();
for(int i=0; i<4; ++i)
{
int nx=s.x+go[i][0];
int ny=s.y+go[i][1];
int t=s.turn;
if(i!=s.now) t++;
if(t<=2&&nx==e_x&&ny==e_y) //如果转弯的个数<=2,抵达终点的话,就跳出循环了
{
F=true;
break;
}
/*如果nx,ny不在图的范围内,或者遇到不是0的点或者弯>2,或者弯>已记录的弯*/
if(nx<1||nx>R||ny<1||ny>C||map[nx][ny]!=0||t>2||t>wan[nx][ny])
continue;
wan[nx][ny]=t;
node p(nx,ny,t,i);
Q.push(p); //满足以上苛刻条件的点入队
}
}
}
int main()
{
while(~scanf("%d%d",&R,&C))
{
if(R==0&&C==0) break;
for(int i=1; i<=R; ++i)
for(int j=1; j<=C; ++j)
scanf("%d",&map[i][j]);
scanf("%d",&query);
while(query--)
{
scanf("%d%d%d%d",&s_x,&s_y,&e_x,&e_y);
F=false;
/*只有当前的点有棋子并且两棋子是同类型的才需要bfs*/
if(map[s_x][s_y]==map[e_x][e_y]&&map[s_x][s_y]!=0)
bfs();
if(F) puts("YES");
else puts("NO");
}
}
}