【2011集训队出题】圈地计划
题目
最近房地产商GDOI(Group of Dumbbells Or Idiots)从NOI(Nuts Old Idiots)手中得到了一块开发土地。据了解,这块土地是一块矩形的区域,可以纵横划分为N×M块小区域。GDOI要求将这些区域分为商业区和工业区来开发。根据不同的地形环境,每块小区域建造商业区和工业区能取得不同的经济价值。更具体点,对于第i行第j列的区域,建造商业区将得到Aij收益,建造工业区将得到Bij收益。另外不同的区域连在一起可以得到额外的收益,即如果区域(I,j)相邻(相邻是指两个格子有公共边)有K块(显然K不超过4)类型不同于(I,j)的区域,则这块区域能增加k×Cij收益。经过Tiger.S教授的勘察,收益矩阵A,B,C都已经知道了。你能帮GDOI求出一个收益最大的方案么?
分析
二元关系,最小割。
先把总收益求出来,在减去最小割。
连边,对于两个相邻的点x,y。
源点点S向x连a[x]的边,向y连b[y]的边。
x向汇点T连b[x]的边,y向汇点T连a[y]的边。
x、y之间连c[x]+c[y]的双向边。
#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const long long maxlongint=2147483647;
const long long mo=1000000007;
const long long N=10010;
using namespace std;
long long next[N*7],last[N*7],to[N*7],up[N*7],n,m,d[N*10],dis[N],tot;
long long f[N*7],ans,c[111][111];
long long zz[4][2]=
{
{0,1},
{1,0},
{0,-1},
{-1,0}
};
long long bj(long long x,long long y,long long z)
{
next[++tot]=last[x];
last[x]=tot;
to[tot]=y;
f[tot]=z;
next[++tot]=last[y];
last[y]=tot;
to[tot]=x;
f[tot]=0;
}
bool bfs()
{
memset(dis,0,sizeof(dis));
d[1]=0;
dis[0]=1;
long long head=0,tail=1,k;
while(head<tail)
{
k=d[++head];
for(long long i=last[k];i;i=next[i])
{
long long j=to[i];
if(f[i]>0 && !dis[j])
{
d[++tail]=j;
dis[j]=dis[k]+1;
}
}
}
return dis[n*m+m];
}
long long aug(long long x,long long y)
{
if(x==n*m+m) return y;
long long cross=0;
for(long long i=last[x];i;i=next[i])
{
long long j=to[i];
if(dis[x]+1==dis[j] && f[i]>0)
{
long long o=aug(j,min(y,f[i]));
if(o>0)
{
y-=o;
f[i]-=o;
f[i^1]+=o;
cross+=o;
}
}
}
return cross;
}
int main()
{
scanf("%lld%lld",&n,&m);
tot=1;
for(long long i=1;i<=n;i++)
for(long long j=1;j<=m;j++)
{
long long pos=(i-1)*m+j;
long long x;
scanf("%lld",&x);
ans+=x;
if(i%2 && j%2 || i%2==0 && j%2==0) bj(0,pos,x);
else bj(pos,n*m+m,x);
}
for(long long i=1;i<=n;i++)
for(long long j=1;j<=m;j++)
{
long long pos=(i-1)*m+j;
long long x;
scanf("%lld",&x);
ans+=x;
if(i%2 && j%2 || i%2==0 && j%2==0) bj(pos,n*m+m,x);
else bj(0,pos,x);
}
for(long long i=1;i<=n;i++)
for(long long j=1;j<=m;j++)
scanf("%lld",&c[i][j]);
for(long long i=1;i<=n;i++)
for(long long j=1;j<=m;j++)
for(long long k=0;k<=3;k++)
{
long long xx=i+zz[k][0],yy=j+zz[k][1];
if(xx<1 || yy<1 || xx>n || yy>m) continue;
long long pos=(i-1)*m+j,pos1=(xx-1)*m+yy;
bj(pos,pos1,c[i][j]+c[xx][yy]);
ans+=c[i][j];
}
while(bfs())
ans-=aug(0,maxlongint);
cout<<ans<<endl;
}