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