#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define N 110
#define M 200
using namespace std;
int n, m, a[N];
int dp[N][M][M], cnt, num[M], sum[M];
///dp[i][j][k] 表示第i行的j状态 i-1行的k状态 的最优解
bool conflict(int x) {
return (x&(x<<2)) || (x&(x>>2));///第x-2个和x+2个位是否有人,
// 有的话就不符合条件
}
int getsum(int x) { ///1的个数,即放多少个兵
int sum = 0;
while(x) {
if(x & 1)
sum++;
x >>= 1;
}
return sum;
}
void init() {
cnt = 0;
for(int i = 0; i < (1<<m); i++) { ///预处理,压缩
if(!conflict(i)) {
num[cnt] = i;
sum[cnt++] = getsum(i);
}
}
}
int main() {
freopen("data.in", "r", stdin);
while(~scanf("%d%d", &n, &m)) {
memset(a, 0, sizeof a);
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
int x;
scanf("%d", &x);
if(x == 0)
continue;///方便计算,把0存为1
a[i] |= (1<<j);
}
}
init();
memset(dp, 0, sizeof dp);
for(int i = 0; i < cnt; i++) { ///处理第一行
if((a[0] | num[i]) != a[0])//不符合原来矩阵情况
continue;
dp[0][i][0] = sum[i];
}
///递推,要考虑三行的情况,
for(int i = 1; i < n; i++) { //第一行
for(int j = 0; j < cnt; j++) {
if((a[i] | num[j]) != a[i])
continue; ///第一行与原来矩阵有冲突
for(int k = 0; k < cnt; k++) { ///第二行
if((a[i-1] | num[k]) != a[i-1])//不符合原来矩阵情况
continue;
if(((num[k] << 1) & num[j]) || ((num[k] >> 1) & num[j]))
continue; //i-1行
int Max = -1;
for(int l = 0; l < cnt; l++) { ///第三行
if(num[j] & num[l])
continue;
if(((num[l] << 1) & num[k]) || ((num[l] >> 1) & num[k]))
continue;
Max = max(Max,dp[i-1][k][l]);
}
dp[i][j][k] = Max + sum[j];
}
}
}
int Max = -1;
for(int i = 0; i < cnt; i++)
for(int j = 0; j < cnt; j++)
Max = max(Max, dp[n-1][i][j]);
printf("%d\n",Max);
}
return 0;
}