#include<iostream>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
const int N = 1e5 + 100;
int mov[4][2] = { {-1,0},{0,1},{1,0},{0,-1} };
int cnt = 1, n, m, s, t, maxflows;
int head[N], depth[N], cur[N], val[1010][1010];
queue<int> q;
struct node {
int nxt;
int to;
int flows;
}edge[N << 1];
void add(int u, int v, int flows) {
edge[++cnt].nxt = head[u];
edge[cnt].to = v;
edge[cnt].flows = flows;
head[u] = cnt;
}
bool bfs() {
for (int i = 1; i <= t; i++) depth[i] = -1;
while (!q.empty())q.pop();
q.push(s);
depth[s] = 0;
cur[s] = head[s];
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (edge[i].flows <= 0) continue;
if (depth[v] == -1) {
depth[v] = depth[u] + 1;
q.push(v);
cur[v] = head[v];
}
}
}
return depth[t] != -1;
}
int dfs(int now, int flows) {
if (now == t || flows == 0) return flows;
int ans = 0;
for (int& i = cur[now]; i; i = edge[i].nxt) {
int v = edge[i].to;
if (depth[v] == depth[now] + 1 && edge[i].flows > 0) {
int ret = dfs(v, min(flows, edge[i].flows));
if (ret > 0) {
edge[i].flows -= ret;
edge[i ^ 1].flows += ret;
ans += ret;
flows -= ret;
if (!flows) break;
}
else {
depth[v] = -1;
}
}
}
return ans;
}
void dinic() {
while (bfs()) {
maxflows += dfs(s, inf);
}
}
int get(int x, int y) {
int ans = (x - 1) * n + y;
return ans;
}
int main() {
cin >> n >> m;
t = n * m + 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> val[i][j];
if (val[i][j] == 1) {
add(s, get(i, j), inf);
add(get(i, j), s, 0);
}
else if (val[i][j] == 2) {
add(get(i, j), t, inf);
add(t, get(i, j), 0);
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k < 4; k++) {
int nx = i + mov[k][0];
int ny = j + mov[k][1];
if (nx<1 || nx>n || ny<1 || ny>m) continue;
add(get(i, j), get(nx, ny), 1);
add(get(nx, ny), get(i, j), 0);
}
}
}
dinic();
cout << maxflows << endl;
}