【bzoj2132】【圈地计划】【最小割】

Description

最近房地产商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求出一个收益最大的方案么?

Input

输入第一行为两个整数,分别为正整数N和M,分别表示区域的行数和列数;第2到N+1列,每行M个整数,表示商业区收益矩阵A;第N+2到2N+1列,每行M个整数,表示工业区收益矩阵B;第2N+2到3N+1行,每行M个整数,表示相邻额外收益矩阵C。第一行,两个整数,分别是n和m(1≤n,m≤100);

任何数字不超过1000”的限制

Output

输出只有一行,包含一个整数,为最大收益值。

Sample Input

3 3
1 2 3
4 5 6
7 8 9
9 8 7
6 5 4
3 2 1
1 1 1
1 3 1
1 1 1

Sample Output

81
【数据规模】
对于100%的数据有N,M≤100
题解:
首先对图黑白染色.
如果i是黑点,从源点向i连权值a[i]的边,从i向汇点连权值b[i]的边。
如果i是白点,从源点向i连权值b[i]的边,从i向汇点连权值a[i]的边。
相邻点i,j之间连权值c[i]+c[j]的边。
最小割即为损失的最小价值。用总价值减一下即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 110
#define M 1000010
#define INF 0x7fffffff
using namespace std;
int cnt(1),point[N*N+2],next[M],st(1),T,cur[N*N+2],dis[N*N+2],gap[N*N+2];
int a[N][N],b[N][N],c[N][N],n,m,pre[N*N+2],sum,x[4]={1,0,-1,0},y[4]={0,1,0,-1};
bool f; 
struct use{int st,en,v;}e[M];
void add(int x,int y,int v){
 // cout<<x<<' '<<y<<' '<<v<<endl;
  next[++cnt]=point[x];point[x]=cnt;e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
  next[++cnt]=point[y];point[y]=cnt;e[cnt].st=y;e[cnt].en=x;e[cnt].v=0; 
}
int isap(){
 int mn,i,u=1,ans(0);gap[0]=T;
 for (i=1;i<=T;i++) cur[i]=point[i];
 while (dis[st]<T){
  f=false;
  for (i=cur[u];i;i=next[i])
   if (e[i].v&&dis[e[i].en]+1==dis[u]){cur[u]=i;f=true;break;}
  if (f){
  	u=e[i].en;pre[u]=i;
  	if (u==T){
  	 mn=INF;
	 for (i=T;i!=st;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
	 ans+=mn;
	 for (i=T;i!=st;i=e[pre[i]].st) e[pre[i]].v-=mn,e[pre[i]^1].v+=mn; 
  	 u=st;
	}
  }
  else{
  	gap[dis[u]]--;if (!gap[dis[u]]) return ans;
  	for (mn=T*2,i=point[u];i;i=next[i]) if (e[i].v)mn=min(mn,dis[e[i].en]);
    dis[u]=mn+1;gap[dis[u]]++;cur[u]=point[u];if(u!=st) u=e[pre[u]].st;
  }
 }
 return ans;	
}
int cal(int x,int y){return m*(x-1)+y+1;} 
int main(){
 scanf("%d%d",&n,&m);T=n*m+2;
 for (int i=1;i<=n;i++)
  for (int j=1;j<=m;j++)
   scanf("%d",&a[i][j]);
 for (int i=1;i<=n;i++)
  for (int j=1;j<=m;j++)
   scanf("%d",&b[i][j]);
 for (int i=1;i<=n;i++)
  for (int j=1;j<=m;j++)
   scanf("%d",&c[i][j]);	
 for (int i=1;i<=n;i++)
  for (int j=1;j<=m;j++){
   if ((i+j)%2) add(1,cal(i,j),a[i][j]),add(cal(i,j),T,b[i][j]);
   else add(1,cal(i,j),b[i][j]),add(cal(i,j),T,a[i][j]);
   sum+=a[i][j]+b[i][j];
   for (int k=0;k<4;k++){
    int xx=i+x[k],yy=j+y[k];
    if (xx<1||xx>n||yy<1||yy>m) continue;
    add(cal(i,j),cal(xx,yy),c[i][j]+c[xx][yy]);sum+=c[i][j];
   }
  }
  printf("%d\n",sum-isap());
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值