#include <cstdio>
#include <vector>
#include <queue>
FILE *fin, *fout;
#define MAXN 300
#define MAXQ 20000
#define MAXN_2 90000
#define MAXVAL 1000000
int N, Q;
int a[MAXN + 1][MAXN + 1];
int x1[MAXQ + 1], Y1[MAXQ + 1], x2[MAXQ + 1], y2[MAXQ + 1];
int st[MAXQ + 1], dr[MAXQ + 1], viz[MAXN + 1][MAXN + 1];
std::vector <int> check[MAXVAL + 1];
struct at {
int x, y;
};
std::queue <at> q;
const int dx[] = {0, 1, 0, -1};
const int dy[] = {1, 0, -1, 0};
inline int maxi(int a, int b) {
if (a > b)
return a;
return b;
}
inline bool bfs(int who, int limit) {
if (a[x1[who]][Y1[who]] < limit || a[x2[who]][y2[who]] < limit)
return 0;
q = std::queue <at>();
q.push({ x1[who], Y1[who] });
memset(viz, 0, sizeof viz);
viz[x1[who]][Y1[who]] = 1;
while (!q.empty()) {
at nod = q.front();
q.pop();
for (int j = 0; j < 4; j++) {
int X = nod.x + dx[j], Y = nod.y + dy[j];
if (viz[X][Y] == 0 && a[X][Y] >= limit) {
viz[X][Y] = 1;
q.push({ X, Y });
}
if (X == x2[who] && Y == y2[who])
return 1;
}
}
return 0;
}
int main() {
fin = fopen("matrice2.in", "r");
fout = fopen("matrice2.out", "w");
fscanf(fin, "%d%d", &N, &Q);
for(int i = 1;i <= N;i++)
for (int j = 1; j <= N; j++) {
fscanf(fin, "%d", &a[i][j]);
}
for (int i = 1; i <= Q; i++) {
fscanf(fin, "%d%d%d%d", &x1[i], &Y1[i], &x2[i], &y2[i]);
st[i] = 1, dr[i] = MAXVAL;
}
bool ok = 1;
int pana;
int pasi = 0;
while (ok) {
ok = 0;
pana = 0;
for (int i = 1; i <= Q; i++) {
if (st[i] != dr[i]) {
check[(st[i] + dr[i]) / 2].push_back(i), pana = maxi(pana, (st[i] + dr[i]) / 2);
// printf("%d %d %d\n", i, st[i], dr[i]);
}
}
for (int i = 1; i <= pana; i++) {//iau fiecare pas al cautarii binare
while (check[i].size()) {
ok = 1;
int who = check[i].back();
check[i].pop_back();
bool ver = bfs(who, i);
if (ver == 1) {
st[who] = i + 1;
}
else
dr[who] = i;
}
}
}
for (int i = 1; i <= Q; i++)
fprintf(fout, "%d\n", st[i] - 1);
fclose(fin);
fclose(fout);
return 0;
}