#include <bits/stdc++.h>
#define pr(p) printf("%d\n",p)
const int oo=2139063143;
const int N=1010000;
const int mod=1000000007;
using namespace std;
typedef long long LL;
typedef double db;
inline void sc (int &x)
{
x=0; static int p; p=1; static char c; c=getchar();
while (!isdigit(c)) { if (c=='-') p=-1; c=getchar(); }
while ( isdigit(c)) { x=(x<<1)+(x<<3)+(c-48); c=getchar(); }
x*=p;
}
int s,t;
struct EDGE { int v,w,nx; }lb[N<<1]; int tot=1,top[N],cur[N];
void add (int u,int v,int w)
{
lb[++tot].v=v,lb[tot].w=w,lb[tot].nx=top[u],top[u]=tot;
lb[++tot].v=u,lb[tot].w=0,lb[tot].nx=top[v],top[v]=tot;
}
queue <int> q;
int d[N];
bool bfs ()
{
memset (d,0,sizeof (d));
memcpy (cur,top,sizeof (top));
bool ly=0;
q.push (s),d[s]=1;
while (q.size ())
{
int u=q.front (); q.pop ();
for (int kb=top[u]; kb!=-1; kb=lb[kb].nx)
{
int v=lb[kb].v,w=lb[kb].w;
//if (v<=0) continue;
if (w&&!d[v])
{
d[v]=d[u]+1;
q.push (v);
ly|=(v==t);
}
}
}
return ly;
}
int dinic (int u,int flow)
{
if (u==t) return flow;
int rest=flow;
for (; cur[u]!=-1&&rest; cur[u]=lb[cur[u]].nx)
{
int c=cur[u];
int v=lb[c].v,w=lb[c].w;
if (w&&d[v]==d[u]+1)
{
int k=dinic (v,min (rest,w));
if (!k) d[v]=0;
lb[c].w-=k;
lb[c^1].w+=k;
rest-=k;
if (!rest) break;
}
}
return flow-rest;
}
const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m;
bool in (int x,int y) { return (x> 0)&&(y> 0)&&(x<=n)&&(y<=m); }
int bh (int x,int y) { return (x-1)*m+y; }
int main()
{
//freopen (".in","r",stdin);
//freopen (".out","w",stdout);
memset (top,-1,sizeof (top));
sc(n),sc(m);
s=n*m+1,t=s+1;
int ans=0;
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
{
int sb; sc(sb); ans+=sb;
((i+j)&1)?add (s,bh (i,j),sb):add (bh(i,j),t,sb);
if ((i+j)&1)
for (int d=0; d< 4; d++)
if (in (i+dx[d],j+dy[d])) add (bh (i,j),bh (i+dx[d],j+dy[d]),oo/10);
}
int flow;
while ((bfs ()))
while ((flow=dinic (s,oo))) ans-=flow;
pr(ans);
return 0;
}
n
个点,可以选或不选,有相应的代价或收益。
某些点之间有特定联系。
最小割。
给每一个点x
连两条边,一条S-x
,一条x-T
。
如果割掉S-x
的边,代表选点x
;割掉x-T
的边,代表不选点x
。
S-x
的边权,就是选点x的代价,x-T
的边权,就是不选点x
的代价。
由于一个点要么选要么不选,在最小割上这两条边肯定会割掉一条。
割掉一条边后,就不可能有从S->x->T
的流了,所以另外一条在最小割上不可能被割掉。
由于最小割只能是代价,所以要把代价转换成收益。
如果选点x
能得到a
的收益,即不选点a
的代价是失去a
的收益。
一开始在答案里加上a
,不选点x
的代价为a
,即边x-T
的边权为a
。
即一开始默认获得所有的收益。
依赖关系:若选点x
,就必须选点y
。
即若选了点x
,又不选点y
,就要付出无穷大的代价。
选了点x
,那么会割掉S-x
,x
和T
连通。
不选点y
,那么会割掉y-T
,y
和S
连通。
那么连y-x
,边权无穷大。若要只选xy
中的一个,为了构成一个割,就得割掉中间的无穷大的边。
不选点x
就不选点y
方法类似。
要求x
的状态和y
相同的情况,就把二者结合起来。
如果选了点x
,就要选点y
,否则就要付出a
的代价来解除这个条件。
连y-x
,边权a
即可。
若将一个组合中所有点都选了(或都没选),那么可以得到一定的收益。
多开一个点x
,代表这个组合。
同样将收益转换成代价,若一个点没有选,就失去这个收益。
即:要选点x
,就必须选组合中的所有点。
从组合中的每一个点连到点x
,边权无穷大,点x
连到点T
,边权为收益。
若组合中有任意一个点y
没有选,则y-T
边会被割掉,y
和S
连通。
由于y
和x
一定连通,为了不让x
和T
连通,就必须割掉x-T
,失去收益。
CC 原创文章采用CC BY-NC-SA 4.0协议进行许可,转载请注明:“转载自:Dinic”