#include <iostream>
#include <fstream>
#include <iomanip>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <unordered_map>
#if 1
#define pv(x) cout<<#x<<" = "<<x<<"; ";cout.flush()
#define pn cout<<endl
#else
#define pv(x)
#define pn
#endif
using namespace std;
ifstream in("pirati.in");
ofstream out("pirati.out");
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
const int NMax = 1e3 + 50;
const int sMax = 25e4 + 5;
typedef pair<int,int> point;
int N,M,Q,nrComp,nrEuler;
int code[NMax][NMax];
int depth[sMax],dad[sMax],euler[sMax],pos[sMax],lg[sMax],rmq[sMax][20],disjointDad[sMax],nr[sMax];
vector<int> v[sMax];
map<pair<int,int>,bool> adj;
const int dx[8] = {-1,-1,-1,0,0,+1,+1,+1},dy[8] = {-1,0,+1,-1,+1,-1,0,+1};
void lee(int,int);
void getEdges();
void dfs(int);
int findRoot(int);
void unite(int,int);
int main() {
in>>N>>M>>Q;
for (int i=1;i <= N;++i) {
for (int j=1;j <= M;++j) {
int idx = (i-1) * M + j;
disjointDad[idx] = idx;
nr[idx] = 1;
char c;
in>>c;
code[i][j] = c - '0';
}
}
/*
for (int i=1;i <= N;++i) {
for (int j=1;j <= M;++j) {
cout<<code[i][j]<<' ';
}
cout<<'\n';
}
cout<<'\n';
//*/
nrComp = 10;
for (int i=1;i <= N;++i) {
for (int j=1;j <= M;++j) {
if (code[i][j] <= 1) {
++nrComp;
lee(i,j);
}
}
}
for (int i=0;i <= nrComp;++i) {
depth[i] = dad[i] = 0;
}
depth[11] = 1;
dfs(11);
lg[1] = 0;
rmq[1][0] = euler[1];
for (int i=2;i <= nrEuler;++i) {
lg[i] = lg[i/2] + 1;
rmq[i][0] = euler[i];
}
for (int p=1;(1<<p) <= nrEuler;++p) {
for (int i=1;i + (1<<p) - 1 <= nrEuler;++i) {
int pos1 = rmq[i][p-1],
pos2 = rmq[i+(1<<(p-1))][p-1];
if (depth[pos1] < depth[pos2]) {
rmq[i][p] = pos1;
}
else {
rmq[i][p] = pos2;
}
}
}
/*
for (int i=1;i <= N;++i) {
for (int j=1;j <= M;++j) {
cout<<code[i][j]<<' ';
}
cout<<'\n';
}
cout<<'\n';
//*/
/*
for (int i=11;i <= nrComp;++i) {
for (int node : v[i]) {
cout<<node<<' ';
}
cout<<'\n';
}
cout<<'\n';
//*/
/*
for (int i=11;i <= nrComp;++i) {
pv(i);pv(depth[i]);pv(dad[i]);pn;
}
cout<<'\n';
//*/
while (Q--) {
int x1,y1,x2,y2;
in>>x1>>y1>>x2>>y2;
int node1 = code[x1][y1], node2 = code[x2][y2];
int pos1 = pos[node1], pos2 = pos[node2];
if (pos1 > pos2) {
swap(pos1,pos2);
}
int pw = lg[pos2 - pos1 + 1], lca;
pos1 = rmq[pos1][pw], pos2 = rmq[pos2 - (1<<pw) + 1][pw];
if (depth[pos1] < depth[pos2]) {
lca = pos1;
}
else {
lca = pos2;
}
out<<(depth[node1] - depth[lca] + depth[node2] - depth[lca])/2<<'\n';
}
return 0;
}
void lee(int x,int y) {
queue<point> Q;
int curr = code[x][y];
Q.push(point(x,y));
code[x][y] = nrComp;
while (Q.size()) {
point p = Q.front();
Q.pop();
//pv(p.first);pv(p.second);pn;
for (int k=0;k < 8;++k) {
int nx = p.first + dx[k],
ny = p.second + dy[k];
if (nx < 1 || nx > N || ny < 1 || ny > M) {
continue;
}
point neighbour(nx,ny);
if (curr == code[nx][ny]) {
code[nx][ny] = nrComp;
Q.push(neighbour);
}
else if (code[nx][ny] > 1 && code[nx][ny] != nrComp) {
int comp1 = nrComp, comp2 = code[nx][ny];
if (findRoot(comp1) == findRoot(comp2)) {
continue;
}
unite(comp1,comp2);
v[comp1].pb(comp2);
v[comp2].pb(comp1);
}
}
}
}
void dfs(int node) {
euler[++nrEuler] = node;
pos[node] = nrEuler;
for (int nxt : v[node]) {
if (depth[nxt]) {
continue;
}
dad[nxt] = node;
depth[nxt] = depth[node] + 1;
dfs(nxt);
euler[++nrEuler] = node;
}
}
int findRoot(int node) {
if (disjointDad[node] == node) {
return node;
}
int root = findRoot(disjointDad[node]);
disjointDad[node] = root;
return root;
}
void unite(int x,int y) {
int rootX = findRoot(x),
rootY = findRoot(y);
if (nr[rootX] > nr[rootY]) {
nr[rootX] += nr[rootY];
nr[rootY] = 0;
disjointDad[rootY] = rootX;
}
else {
nr[rootY] += nr[rootX];
nr[rootX] = 0;
disjointDad[rootX] = rootY;
}
}