有个N*M的矩形,每个区域选择两种类型,分别获得Ai,j和Bi,j的收益。一个格子如果有与其相邻的k个格子与其类型不同,则可以带来Ci,j的收益。求最大收益。
由于是最大值,所以不能直接求最小割。
可以把边权取反,再求最小割。但是流量不能是负数,可以先把答案加上总和。
详细讲解请看wzd的博客
http://blog.csdn.net/werkeytom_ftd/article/details/49836765
#include
#include
#include
#include
#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]]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]]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);
}