#include <iostream>
#include <fstream>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
ifstream fin("matrice2.in");
ofstream fout("matrice2.out");
#define swich(i, j) n * (i - 1) + j
int t[90001], n, k, rez[20001], mat[301][301], nn;
struct cb{
int x1, y1, x2, y2, l, r, m;
int poz;
bool operator < (const cb& aux) const
{
return m > aux.m;
}
}q[20001];
struct element{
int i, j, val;
bool operator < (const element& aux) const
{
return val > aux.val;
}
} a[90001];
int sz; // a size
int root(int x)
{
if (t[x] == x)
return x;
return t[x] = root(t[x]);
}
int d1[] = {0, -1, 0, 1, 0};
int d2[] = {0, 0, 1, 0, -1};
int main()
{
fin >> n >> k;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
fin >> mat[i][j];
a[++sz] = {i, j, mat[i][j]};
}
sort(a + 1, a + sz + 1);
nn = n * n;
for (int i = 1; i <= k; i++)
{
int x1, y1, x2, y2;
fin >> x1 >> y1 >> x2 >> y2;
q[i] = {x1, y1, x2, y2, 1, 9, 5, i}; // FIX ME
}
sort(q + 1, q + k + 1);
bool running;
int r1, r2; // use for roots
int x, y;
do{
// disjoint sets initialization
for (int i = 1; i <= nn; i++)
t[i] = i;
running = false;
int j = 1; // index of array 'a' (elements of matrix)
for (int i = 1; i <= k && q[i].m; i++)
{
running = true;
while (j <= sz && a[j].val >= q[i].m)
{
r1 = root(swich(a[j].i, a[j].j));
for (int l = 1; l <= 4; l++)
{
x = a[j].i + d1[l];
y = a[j].j + d2[l];
if (x < 0 || x > n || y < 0 || y > n || mat[x][y] < q[i].m) // not valid poz
continue;
t[root((swich(x, y)))] = r1; // unit roots
}
j++;
}
if (root(swich(q[i].x1, q[i].y1)) == root(swich(q[i].x2, q[i].y2)))
{
q[i].l = q[i].m + 1;
rez[q[i].poz] = q[i].m;
}
else
q[i].r = q[i].m - 1;
if (q[i].l > q[i].r)
q[i].m = 0;
else
q[i].m = ((q[i].l + q[i].r) >> 1);
}
sort(q + 1, q + k + 1);
}while (running);
for (int i = 1; i <= k; i++)
fout << rez[i] << '\n';
return 0;
}