#include<fstream>
#include<vector>
#include<algorithm>
using namespace std;
typedef int var;
ifstream fin("matrice2.in");
ofstream fout("matrice2.out");
struct Edge {
var a, b, c;
Edge(var x, var y, var z) {
a = x;
b = y;
c = z;
}
const bool operator<(const Edge &e) const{
return c > e.c;
}
};
#define MAXN 305
#define MAX_NO 305*305*4
var n, m;
var A[MAXN][MAXN];
vector<var> G[MAX_NO];
vector<Edge> E;
var COST[MAX_NO], PARENT[MAX_NO];
var Parent[MAXN*MAXN], Rank[MAXN*MAXN], Node[MAXN*MAXN];
var Find(var f) {
if(!Parent[f]) return f;
Parent[f] = Find(Parent[f]);
return Parent[f];
}
void Union(var r1, var r2, var under) {
r1 = Find(r1);
r2 = Find(r2);
if(Rank[r1] < Rank[r2]) {
Parent[r1] = r2;
Node[r2] = under;
} else {
Parent[r2] = r1;
Node[r1] = under;
Rank[r1] += (Rank[r1] == Rank[r2]);
}
}
var toNod(var i, var j) {
return n*(i-1)+j;
}
void afisEdge(Edge e) {
var i1 = (e.a - 1) / n + 1;
var j1 = (e.a - 1) % n + 1;
var i2 = (e.b - 1) / n + 1;
var j2 = (e.b - 1) % n + 1;
fout<<i1<<" "<<j1<<" | "<<i2<<" "<<j2<<" : "<<e.c<<'\n';
}
struct Rmq {
var rmq, sol;
Rmq(var a, var b) {
sol = a;
rmq = b;
}
Rmq() {}
const bool operator <(const Rmq &r) const {
return rmq < r.rmq;
}
};
var tm;
var B[MAX_NO];
Rmq Euler[3*MAX_NO];
void euler(var node, var depth) {
B[node] = ++tm;
Euler[tm] = Rmq(node, depth);
for(auto vec : G[node]) {
if(!B[vec]) {
euler(vec, depth+1);
Euler[++tm] = Rmq(node, depth);
}
}
}
Rmq *Tree;
void build_tree(var node, var b, var e) {
if(b == e) Tree[node] = Euler[b];
else {
var m = (b+e)/2;
build_tree(node*2, b, m);
build_tree(node*2+1, m+1, e);
Tree[node] = min(Tree[node*2], Tree[node*2+1]);
}
}
Rmq query(var node, var b, var e, var l, var r) {
if(b >= l && e <= r) return Tree[node];
if(l > e || r < b) return Rmq((1<<29), (1<<29));
var m = (b+e) / 2;
return min(query(node*2, b, m, l, r), query(node*2+1, m+1, e, l, r));
}
var lca(var a, var b) {
a = B[a];
b = B[b];
if(a > b) swap(a, b);
Rmq r = query(1, 1, tm, a, b);
return r.sol;
}
int main() {
fin>>n>>m;
for(var i=1; i<=n; i++) {
for(var j=1; j<=n; j++) {
fin>>A[i][j];
}
}
/*
for(var i=1; i<=n; i++) {
for(var j=1; j<=n; j++) {
var t = toNod(i, j);
var i1 = (t - 1) / n + 1;
var j1 = (t - 1) % n + 1;
fout<<i1<<" "<<j1<<" ; ";
}
fout<<endl;
}
*/
for(var i=1; i<n; i++) {
for(var j=1; j<n; j++) {
E.push_back(Edge(toNod(i, j), toNod(i+1, j), min(A[i][j], A[i+1][j])));
E.push_back(Edge(toNod(i, j), toNod(i, j+1), min(A[i][j], A[i][j+1])));
}
}
for(var i=1; i<n; i++) {
E.push_back(Edge(toNod(n, i), toNod(n, i+1), min(A[n][i], A[n][i+1])));
E.push_back(Edge(toNod(i, n), toNod(i+1, n), min(A[i][n], A[i+1][n])));
}
sort(E.begin(), E.end());
var nodes = n*n;
for(var i=1; i<=nodes; i++) {
Node[i] = i;
}
for(auto e : E) {
if(Find(e.a) != Find(e.b)) {
PARENT[Node[Find(e.a)]] = PARENT[Node[Find(e.b)]] = ++nodes;
G[nodes].push_back(Node[Find(e.a)]);
G[nodes].push_back(Node[Find(e.b)]);
COST[nodes] = e.c;
Union(e.a, e.b, nodes);
}
}
euler(nodes, 0);
for(var i=1; i<=nodes; i++) {
G[i].clear();
}
Tree = new Rmq[12*nodes*nodes];
build_tree(1, 1, tm);
var a, b, c, d;
while(m--) {
fin>>a>>b>>c>>d;
var u = toNod(a, b);
var v = toNod(c, d);
fout<<COST[lca(u, v)]<<'\n';
}
return 0;
}