/*
这题和poj的1185很像
http://blog.csdn.net/cq_pf/article/details/41931061
dp[i][j][k] 第i行在第i-1行的状态j,第i行的状态为k时的最大值
状态转移方程dp[i][j][k]=max(dp[i-1][s][j]+count(k))
count(k)为二进制状态为k时的取值
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 110
#define maxs 200
#define maxm 20
int dp[maxn][maxs][maxs];
int landstatus[maxn];//每一行的状态
int useful[maxs];//有用的状态
int n,m;
int count(int x)
{
int ans=0;
while(x)
{
if(x%2)
ans++;
x/=2;
}
return ans;
}
int init()
{
int upper=1<<m;
int i,k=0;
for(i=0;i<upper;i++)
{
if((i&(i<<2)))//与poj1185相比在判断有用的状态时只需要判断移两位的情况
continue;
useful[k++]=i;
}
return k;
}
int main()
{
int i,j,k;int s;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(landstatus,0,sizeof(landstatus));
memset(dp,-1,sizeof(dp));
for(i=0;i<n;i++)
{
int temp;
for(j=0;j<m;j++)
{
scanf("%d",&temp);
if(!temp)
landstatus[i]+=(1<<j);
}
}
int topper=init();
for(j=0;j<topper;j++)
{
if(useful[j]&landstatus[0])
continue;
dp[0][0][j]=count(useful[j]);
}
for(i=1;i<n;i++)
for(k=0;k<topper;k++)
{
if(useful[k]&landstatus[i])
continue;
for(j=0;j<topper;j++)
{
if((useful[j]&(useful[k]<<1))||(useful[j]&(useful[k]>>1)))//与poj之间
continue; //相比判断前一行和
for(s=0;s<topper;s++) //和该行的状态只需判断
{ //左移一位和右移一位
if(useful[k]&useful[s]) //的状态
continue;
if(dp[i-1][s][j]==-1)
continue;
dp[i][j][k]=max(dp[i-1][s][j]+count(useful[k]),dp[i][j][k]);
}
}
}
int ans=0;
for(j=0;j<topper;j++)
for(k=0;k<topper;k++)
ans=max(ans,dp[n-1][j][k]);
printf("%d\n",ans);
}
return 0;
}