圈地计划

题目大意

有个N*M的矩形,每个区域选择两种类型,分别获得Ai,j和Bi,j的收益。一个格子如果有与其相邻的k个格子与其类型不同,则可以带来Ci,j的收益。求最大收益。

二元关系

由于是最大值,所以不能直接求最小割。
可以把边权取反,再求最小割。但是流量不能是负数,可以先把答案加上总和。
详细讲解请看wzd的博客

http://blog.csdn.net/werkeytom_ftd/article/details/49836765

代码

#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=800+5;
int sum,b1,bz[maxn*maxn],d[maxn*maxn],now[maxn*maxn],ans,re[maxn*maxn],c1[maxn][maxn],next[maxn*maxn];
int i,j,n,m,a[maxn][maxn],b[maxn][maxn],num,st,en,k[maxn*maxn],g[maxn*maxn],c[maxn*maxn],w[5][3],l;
void add(int x,int y,int z){
    next[++num]=k[x];
    k[x]=num;g[num]=y;c[num]=z;
    re[num]=++num;re[num]=num-1;
    next[num]=k[y];
    k[y]=num;g[num]=x;c[num]=0;
}
int dfs(int x,int a){
    if (a==0) return 0;
    if (x==en) {
        ans+=a;
        return a;
    }
    bz[x]=b1;
    int i=now[x];
    while (i){
        if ((bz[g[i]]<b1)&&(d[x]==d[g[i]]+1)&&(c[i]>0)){
            int j=dfs(g[i],min(a,c[i]));
            if (j){
                now[x]=i;
                c[re[i]]+=j;
                c[i]-=j;
                return j;
            }
        }
        i=next[i];
    }
    now[x]=0;
    return 0;
}
bool update(){
    int dis=en,j;
    fo(j,1,en) if (bz[j]==b1){
        int i=k[j];
        while (i){
            if ((bz[g[i]]<b1)&&(c[i]>0)) dis=min(dis,d[g[i]]+1-d[j]);
            i=next[i];
        }
    }
    if (dis==en) return 0;
    fo(j,1,en) if (bz[j]==b1) d[j]+=dis;
    return 1;
}
int main(){
    scanf("%d%d",&n,&m);
    w[1][1]=0,w[1][2]=1,w[2][1]=0,w[2][2]=-1,w[3][1]=1,w[3][2]=0,w[4][1]=-1,w[4][2]=0;
    fo(i,1,n)
    fo(j,1,m) scanf("%d",&a[i][j]); 
    fo(i,1,n)
    fo(j,1,m) scanf("%d",&b[i][j]); 
    fo(i,1,n)
    fo(j,1,m) scanf("%d",&c1[i][j]);
    st=1,en=n*m+2;
    fo(i,1,n){
        int co=i%2;
        fo(j,1,m){
            int n1=(i-1)*m+j+1;
            if (co) add(st,n1,a[i][j]),add(n1,en,b[i][j]);
            else add(st,n1,b[i][j]),add(n1,en,a[i][j]);
            sum+=a[i][j]+b[i][j];
            fo(l,1,4){
                int i1=i+w[l][1],j1=j+w[l][2];
                if ((!i1)||(!j1)) continue;
                if ((i1>n)||(j1>m)) continue;
                int n2=(i1-1)*m+j1+1;
                sum+=c1[i][j];
                add(n1,n2,c1[i][j]+c1[i1][j1]);
            }
            co=1-co;
        }
    }b1=0;
    while (1){
        b1++;
        fo(i,1,en) now[i]=k[i];
        while (dfs(1,sum)) b1++; 
        if (!update()) break;
    }
    printf("%d\n",sum-ans);
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值