【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;
}

你可能感兴趣的:(【2011集训队出题】圈地计划)