2132: 圈地计划
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 382 Solved: 271
[ 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);
Output
输出只有一行,包含一个整数,为最大收益值。
Sample Input
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
【数据规模】
对于100%的数据有N,M≤100
最小割建模。
要求收益最大,但是最小割是最小,所以肯定是用总收益减去最小的不可行收益。
如果两个相邻格子颜色一样,是需要被减掉的。
但是最小割中x与y连边,如果xy不在同一集合才会产生代价,因此我们需要把这块土地黑白染色,黑色格子属于s集表示商业区,白色格子属于s集表示工业区。
因此,黑白染色,然后就是最小割的基本建模了。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <cstdlib>
#define M 100+5
#define N 20000+5
#define inf 0x3f3f3f3f
using namespace std;
int tot=1,s,t,h[N],cur[N],d[N],v[N],num[M][M],c[M][M],n,m,A[M][M],B[M][M],C[M][M];
int fx[6][3];
struct edge
{
int from,to,cap,flow,ne;
}E[200005];
void Addedge(int from,int to,int cap)
{
E[++tot]=(edge){from,to,cap,0,h[from]};
h[from]=tot;
E[++tot]=(edge){to,from,0,0,h[to]};
h[to]=tot;
}
bool bfs()
{
for (int i=s;i<=t;i++)
v[i]=0;
v[s]=1;
d[s]=0;
queue<int> q;
q.push(s);
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=h[x];i;i=E[i].ne)
{
edge e=E[i];
if (!v[e.to]&&e.cap>e.flow)
{
v[e.to]=1;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return v[t];
}
int dfs(int x,int a)
{
if (x==t||!a) return a;
int flow=0;
for (int &i=cur[x];i;i=E[i].ne)
{
edge &e=E[i];
if (d[e.to]!=d[x]+1) continue;
int f=dfs(e.to,min(a,e.cap-e.flow));
if (f)
{
e.flow+=f;
E[i^1].flow-=f;
flow+=f;
a-=f;
if (!a) break;
}
}
return flow;
}
int dinic()
{
int flow=0;
while (bfs())
{
for (int i=s;i<=t;i++)
cur[i]=h[i];
flow+=dfs(s,inf);
}
return flow;
}
void Paint()
{
for (int i=1;i<=m;i++)
c[0][i]=1^c[0][i-1];
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
c[i][j]=1^c[i-1][j],num[i][j]=(i-1)*m+j;
}
int main()
{
scanf("%d%d",&n,&m);
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]);
Paint();
s=0,t=n*m+1;
int ans=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (c[i][j])
Addedge(s,num[i][j],A[i][j]),Addedge(num[i][j],t,B[i][j]);
else
Addedge(s,num[i][j],B[i][j]),Addedge(num[i][j],t,A[i][j]);
ans+=(A[i][j]+B[i][j]);
}
fx[1][1]=fx[2][1]=0,fx[1][2]=1,fx[2][2]=-1;
fx[3][2]=fx[4][2]=0,fx[3][1]=1,fx[4][1]=-1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
for (int k=1;k<=4;k++)
{
int nx=i+fx[k][1],ny=j+fx[k][2];
if (nx<1||ny<1||nx>n||ny>m) continue;
int x=C[i][j]+C[nx][ny];
ans+=C[i][j];
Addedge(num[i][j],num[nx][ny],x);
}
}
printf("%d\n",ans-dinic());
return 0;
}