bzoj 2132(最小割)

11 篇文章 0 订阅

2132: 圈地计划

Time Limit: 2 Sec   Memory Limit: 256 MB
Submit: 808   Solved: 362
[ 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


解题思路:和2172简直一模一样,不过相邻的两条边,要将商业边和工业边反过来,

可以黑白染色,然后建边,最后最大流就可以了。


#include <cstdio> 
#include <cstring> 
#include <iostream> 
#include <algorithm> 
#define M 10100 
#define P(x,y) ((x)*n-n+(y)) 
#define S 0 
#define T (m*n+1) 
#define INF 0x3f3f3f3f 
using namespace std; 
int m,n,ans; 
const int dx[]={1,-1,0,0}; 
const int dy[]={0,0,1,-1}; 
namespace Max_Flow{ 
     struct abcd{ 
         int to,f,next; 
     }table[1001001]; 
     int head[M],tot=1; 
     int dpt[M]; 
     void Add( int x, int y, int z) 
    
         table[++tot].to=y; 
         table[tot].f=z; 
         table[tot].next=head[x]; 
         head[x]=tot; 
    
     void Link( int x, int y, int z) 
    
         Add(x,y,z); 
         Add(y,x,z); 
    
     bool BFS() 
    
         static int q[M]; 
         int i,r=0,h=0; 
         memset (dpt,-1, sizeof dpt); 
         dpt[S]=1;q[++r]=S; 
         while (r!=h) 
        
             int x=q[++h]; 
             for (i=head[x];i;i=table[i].next) 
                 if (table[i].f&&!~dpt[table[i].to]) 
                
                     dpt[table[i].to]=dpt[x]+1; 
                     q[++r]=table[i].to; 
                     if (table[i].to==T) 
                         return true
                
        
         return false
    
     int Dinic( int x, int flow) 
    
         int i,left=flow; 
         if (x==T) return flow; 
         for (i=head[x];i&&left;i=table[i].next) 
             if (table[i].f&&dpt[table[i].to]==dpt[x]+1) 
            
                 int temp=Dinic(table[i].to,min(left,table[i].f) ); 
                 left-=temp; 
                 table[i].f-=temp; 
                 table[i^1].f+=temp; 
            
         if (left) dpt[x]=-1; 
         return flow-left; 
    
int main() 
     using namespace Max_Flow; 
     int i,j,k,x; 
     cin>>m>>n; 
     for (i=1;i<=m;i++) 
         for (j=1;j<=n;j++) 
        
             scanf ( "%d" ,&x); 
             ans+=x; 
             if (i+j&1) 
                 Link(S,P(i,j),x); 
             else 
                 Link(P(i,j),T,x); 
        
     for (i=1;i<=m;i++) 
         for (j=1;j<=n;j++) 
        
             scanf ( "%d" ,&x); 
             ans+=x; 
             if (~(i+j)&1) 
                 Link(S,P(i,j),x); 
             else 
                 Link(P(i,j),T,x); 
        
     for (i=1;i<=m;i++) 
         for (j=1;j<=n;j++) 
        
             scanf ( "%d" ,&x); 
             for (k=0;k<4;k++) 
            
                 int xx=i+dx[k]; 
                 int yy=j+dy[k]; 
                 if (xx<=0||yy<=0||xx>m||yy>n) 
                     continue
                 ans+=x; 
                 Link(P(i,j),P(xx,yy),x); 
            
        
     while ( BFS() ) 
         ans-=Dinic(S,INF); 
     cout<<ans<<endl; 
     return 0; 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值