bzoj2132 圈地计划

2132: 圈地计划

Time Limit: 2 Sec   Memory Limit: 256 MB
Submit: 661   Solved: 295
[ Submit][ Status][ Discuss]

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

HINT

 数据已加强,并重测--2015.5.15

Source




和bzoj1976能量魔方类似,不过这道题是二维的。

我们先将所有点黑白染色。对于黑点i,从s到i连权值为a[i]的边,从i到t连权值为b[i]的边;对于白点i,从s到i连权值为b[i]的边,从i到t连权值为a[i]的边。对于相邻的两个点i和j,从i到j、从j到i分别连权值为c[i]+c[j]的边。

最后跑一次最小割,从最初的总收益中减去最小割。




#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define pa pair<int,int>
#define maxn 10100
#define maxm 100100
#define inf 1000000000
#define f(x,y) (x-1)*m+y
using namespace std;
struct edge_type
{
	int next,to,v;
}e[maxm];
int head[maxn],cur[maxn],dis[maxn],a[105][105];
int n,m,s,t,x,cnt=1,ans=0,tot=0;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void add_edge(int x,int y,int v1,int v2)
{
	e[++cnt]=(edge_type){head[x],y,v1};head[x]=cnt;
	e[++cnt]=(edge_type){head[y],x,v2};head[y]=cnt;
}
inline bool bfs()
{
	queue<int>q;
	memset(dis,-1,sizeof(dis));
	dis[s]=0;q.push(s);
	while (!q.empty())
	{
		int tmp=q.front();q.pop();
		if (tmp==t) return true;
		for(int i=head[tmp];i;i=e[i].next) if (e[i].v&&dis[e[i].to]==-1)
		{
			dis[e[i].to]=dis[tmp]+1;
			q.push(e[i].to);
		}
	}
	return false;
}
inline int dfs(int x,int f)
{
	if (x==t) return f;
	int tmp,sum=0;
	for(int &i=cur[x];i;i=e[i].next)
	{
		int y=e[i].to;
		if (e[i].v&&dis[y]==dis[x]+1)
		{
			tmp=dfs(y,min(f-sum,e[i].v));
			e[i].v-=tmp;e[i^1].v+=tmp;sum+=tmp;
			if (sum==f) return sum;
		}
	}
	if (!sum) dis[x]=-1;
	return sum;
}
inline void dinic()
{
	while (bfs())
	{
		F(i,1,t) cur[i]=head[i];
		ans+=dfs(s,inf);
	}
}
int main()
{
	n=read();m=read();
	s=n*m+1;t=s+1;
	F(i,1,n) F(j,1,m)
	{
		x=read();
		tot+=x;
		if ((i+j)&1) add_edge(s,f(i,j),x,0);
		else add_edge(f(i,j),t,x,0);
	}
	F(i,1,n) F(j,1,m)
	{
		x=read();
		tot+=x;
		if ((i+j)&1) add_edge(f(i,j),t,x,0);
		else add_edge(s,f(i,j),x,0);
	}
	F(i,1,n) F(j,1,m) a[i][j]=read();
	F(i,1,n) F(j,1,m-1)
	{
		int tmp=a[i][j]+a[i][j+1];
		tot+=tmp;
		add_edge(f(i,j),f(i,j+1),tmp,tmp);
	}
	F(i,1,n-1) F(j,1,m)
	{
		int tmp=a[i][j]+a[i+1][j];
		tot+=tmp;
		add_edge(f(i,j),f(i+1,j),tmp,tmp);
	}
	dinic();
	printf("%d\n",tot-ans);
}


你可能感兴趣的:(网络流,bzoj)