#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <vector>
#include <bitset>
#define MAXN 350
#define MAXQ 20050
#define MAXVAL 1000050
using namespace std;
int n, q, a[MAXN][MAXN];
struct query
{
int x1, y1, x2, y2;
query(int x1 = 0, int y1 = 0, int x2 = 0, int y2 = 0) : x1(x1), y1(y1), x2(x2), y2(y2) { }
};
struct elem
{
int x, val;
elem(int x = 0, int val = 0) : x(x), val(val) { }
bool operator()(elem e, elem f)
{
return e.val > f.val;
}
};
query stion[MAXQ];
int state[MAXQ], trial[MAXQ], ind[MAXQ];
int parent[3*MAXN*MAXN];
struct muchie
{
int x, y, cost;
muchie(int x = 0, int y = 0, int cost = 0) : x(x), y(y), cost(cost) { }
bool operator<(const muchie &e) const
{
return cost > e.cost;
}
};
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
vector<muchie> muh;
vector<pair<int, int> > qries[3*MAXN*MAXN];
void addMuhs(int x, int y)
{
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= n && (a[x][y] <= a[nx][ny]))
muh.push_back(muchie(n*x+y, n*nx+ny, a[x][y]));
}
}
void citire()
{
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &a[i][j]);
for (int i = 1; i <= q; i++) {
scanf("%d %d %d %d", &stion[i].x1, &stion[i].y1, &stion[i].x2, &stion[i].y2);
qries[stion[i].x1*n + stion[i].y1].push_back(make_pair(stion[i].x2*n + stion[i].y2, i));
qries[stion[i].x2*n + stion[i].y2].push_back(make_pair(stion[i].x1*n + stion[i].y1, i));
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
addMuhs(i, j);
sort(muh.begin(), muh.end());
}
int getParent(int x)
{
if (parent[x] == x)
return x;
else
return parent[x] = getParent(parent[x]);
}
int val[3*MAXN*MAXN], repr[3*MAXN*MAXN], nq;
vector<int> v[3*MAXN*MAXN];
void build()
{
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
val[i*n+j] = a[i][j];
parent[i*n+j] = i*n+j;
}
nq = n*n+n;
for (muchie m : muh) {
int p1 = getParent(m.x);
int p2 = getParent(m.y);
if (p1 != p2) {
parent[p1] = ++nq;
parent[p2] = nq;
parent[nq] = nq;
val[nq] = min(val[p1], val[p2]);
v[nq].push_back(p1);
v[nq].push_back(p2);
}
}
}
void debug(int nod)
{
if (nod > n*n+n)
printf("nou ");
else
printf("%d %d ", (nod-1)/5, (nod-1)%5+1);
printf("%d\n", val[nod]);
for (int y : v[nod])
debug(y);
}
struct padure
{
int mat[3*MAXN*MAXN];
void init()
{
for (int i = 1; i <= nq; i++)
mat[i] = i;
}
int par(int x)
{
if (mat[x] == x)
return x;
return mat[x] = par(mat[x]);
}
int unify(int x, int y)
{
mat[par(x)] = par(y);
}
int united(int x, int y)
{
return par(x) == par(y);
}
};
bitset<3*MAXN*MAXN> viz;
padure p;
void solve(int nod)
{
for (int y : v[nod]) {
solve(y);
p.unify(y, nod);
repr[p.par(nod)] = nod;
}
viz[nod] = 1;
for (pair<int, int> pa : qries[nod]) {
if (viz[pa.first])
state[pa.second] = val[repr[p.par(pa.first)]];
}
}
void afisare()
{
for (int i = 1; i <= q; i++)
printf("%d\n", state[i]);
}
int main()
{
freopen("matrice2.in", "r", stdin);
freopen("matrice2.out", "w", stdout);
citire();
build();
//debug(nq);
p.init();
solve(nq);
afisare();
return 0;
}