/*
题意:中文题
!!!与hdu1565一模两样!!!
题解:求最小权点覆盖集;
参考网站:http://yzmduncan.iteye.com/blog/1149057
最小权点覆盖=总权值-最大流
题目要求所选两个方格不能有共边,因此将所有的方格看作顶点,公共边看作连接两个顶点的边,这样
这个方形就转变为了一个无向图;题目要求两两顶点间没有公共边,即通过去除部分顶点来去除所有的
边,剩下的没有边的图即为没有公共边的所有方格,然后保证剩下的权值和最大,亦即求最小权点覆盖
集的权值之和;
建图方法:首先确定该图是一个二分图,然后在此基础上构建有向图,将二分图的两个集合x,y找出,
x和y之间的(u,v)权值变为INF,方向u->v;加入一个起点s和汇点t;s指向x的所有点,y的所有点指
向t,边的权值为该点的值。
然后求出最大流,用总和减去最大流即为最终解。
注意:在建图的时候反向边依旧需要添加。
*/
#include <iostream>
#include <cstring>
#include <queue>
#define EMAX 20050
#define VMAX 2555
using namespace std;
const int INF = 0x7fffffff;
int dir[4][2]={0, 1, 0, -1, 1, 0, -1, 0};
int EN;//边的总数
int head[VMAX];//用邻接表表示图
int pre[VMAX];//记录路径
int path[EMAX];//记录该点边的位置
struct edge
{
int to;
int weight;//流量
int next;
}e[EMAX];
void insert(int u, int v, int w)
{
e[EN].next = head[u];
e[EN].to = v;
e[EN].weight = w;
head[u] = EN++;
e[EN].next = head[v];
e[EN].to = u;
e[EN].weight = 0;
head[v] = EN++;
}
int flow_ek(int s, int t)
{
queue<int> Q;
int ret = 0;
while (true)
{
memset(pre,-1,sizeof(pre));
while (!Q.empty())
Q.pop();
Q.push(s);
while (!Q.empty())//BFS
{
int u = Q.front();
Q.pop();
for(int i=head[u]; i!=-1; i=e[i].next)
{
if (pre[e[i].to]==-1 && e[i].weight>0)
{
pre[e[i].to] = u;//记录路径
path[e[i].to] = i;//记录该点边的所在位置,方便后面直接查询
Q.push(e[i].to);
}
}
if (pre[t] != -1)//终点的pre不为-1,表示找到一条增广路径
break;
}
if (pre[t] == -1)//当BFS后找不到增广路径则结束循环
break;
int mw = -1;
for(int v=t; v!=s; v=pre[v])//找出当前路径中的流量的最小容量
{
if (mw == -1 || mw > e[path[v]].weight)
mw = e[path[v]].weight;
}
for(int v=t; v!=s; v=pre[v])//修改路径的容量,求出残余网络
{
e[path[v]].weight -= mw;
e[path[v]^1].weight += mw;//更新逆向边
}
ret += mw;
}
return ret;
}
int main(void)
{
int n,m,temp;
while (cin >> m >> n)
{
memset(head,-1,sizeof(head));
EN = 0;
int sum = 0;
for(int i=0; i<m; i++)
{
for(int j=1; j<=n; j++)
{
cin >> temp;
sum += temp;
if ((i+j)%2 == 1)//插入于起点、汇点相连的边
insert(0,i*n+j,temp);
else
insert(i*n+j,m*n+1,temp);
}
}
for(int i=0; i<m; i++)
{
for(int j=1; j<=n; j++)
{
if ((i+j)%2 == 1)
{
for(int k=0;k<4;k++)
{
int a=i+dir[k][0];
int b=j+dir[k][1];
if (a>=0&&a<m&&b>=1&&b<=n)//插入二分图之间的有向边
{
insert(i*n+j,a*n+b,INF);
}
}
}
}
}
cout << sum - flow_ek(0,m*n+1) << endl;
}
return 0;
}