bzoj2132: 圈地计划(无比强大的最小割)
2132: 圈地计划
题目:传送门
简要题意:
给出一个矩阵,一共n*m个点,并给出三个收益矩阵。A矩阵表示这个点建A的可取收益,B矩阵表示这个点建B的可取收益,C矩阵表示如果相邻(有且仅有一条公共边)的点和自己所建的建筑不一样,则可获得C[i][j]的收益,如果相邻的有k个点和自己不一样,则收益为k*C[i][j]。求最大收益。
题解:
日常一模最小割%%%ORZ
本蒟蒻其实也看出是最小割什么的,但是怎么割啊。。。
可能有人会和我有一样的疑惑:
按照正常的割法建图:st到x连A收益,x到ed连B收益,两两之间再连C收益
连完之后就蒙B了...割出来的是什么鬼???
这时我们会发现,这样子连的话负权边根本没有体现啊???
%题解啊啊啊:
正解其实是需要进行黑白染色的(原因后面讲)
染完色之后:st到黑点连A收益,黑点到ed连B收益;白点反之,然后相邻的不同颜色的格子相互连边(其实就是每个点还要连出去上下左右的点,因为染色了啊)
这样之后用sum-最小割就OK。
那染色是什么鬼:
正确答案并不一定是相邻的点颜色都不一样,那么染色的目的就不是保证收益最大。
但是染完色之后再跑最小割我们可以发现:
如果某相邻两点异色的收益不如同色的收益,那么这条路径上关于相邻异色的收益肯定会被割掉
如果异色收益更优,那割掉的肯定是一个A收益加一个B收益
那么肯定是要最小割啊~
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #define qread(x) x=read() 7 using namespace std; 8 inline int read() 9 { 10 int f=1,x=0;char ch; 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 13 return f*x; 14 } 15 struct node 16 { 17 int x,y,c,next,other; 18 }a[1110000];int len,last[110000]; 19 int n,m,st,ed,head,tail; 20 void ins(int x,int y,int c) 21 { 22 int k1,k2; 23 k1=++len; 24 a[len].x=x;a[len].y=y;a[len].c=c; 25 a[len].next=last[x];last[x]=len; 26 27 k2=++len; 28 a[len].x=y;a[len].y=x;a[len].c=0; 29 a[len].next=last[y];last[y]=len; 30 31 a[k1].other=k2; 32 a[k2].other=k1; 33 } 34 int list[11000],h[110000]; 35 bool bt_h() 36 { 37 memset(h,0,sizeof(h));h[st]=1; 38 list[1]=st;head=1;tail=2; 39 while(head!=tail) 40 { 41 int x=list[head]; 42 for(int k=last[x];k;k=a[k].next) 43 { 44 int y=a[k].y; 45 if(h[y]==0 && a[k].c>0) 46 { 47 h[y]=h[x]+1; 48 list[tail++]=y; 49 } 50 } 51 head++; 52 } 53 if(h[ed]>0)return true; 54 return false; 55 } 56 int find_flow(int x,int flow) 57 { 58 if(x==ed)return flow; 59 int s=0,t; 60 for(int k=last[x];k;k=a[k].next) 61 { 62 int y=a[k].y; 63 if(h[y]==h[x]+1 && a[k].c>0 && s<flow) 64 { 65 s+=t=find_flow(y,min(a[k].c,flow-s)); 66 a[k].c-=t;a[a[k].other].c+=t; 67 } 68 } 69 if(s==0)h[x]=0; 70 return s; 71 } 72 int A[110][110],B[110][110],C[110][110]; 73 int f[110][110];//黑白染色 1为黑 2为白 74 int d[110][110]; 75 int main() 76 { 77 qread(n);qread(m); 78 len=0;memset(last,0,sizeof(last)); 79 st=n*m+1;ed=st+1; 80 int s=1; 81 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)d[i][j]=(i-1)*m+j; 82 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)qread(A[i][j]); 83 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)qread(B[i][j]); 84 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)qread(C[i][j]); 85 memset(f,0,sizeof(f)); 86 for(int i=1;i<=n;i++) 87 for(int j=1;j<=m;j++) 88 { 89 if(i==1 && j==1)continue; 90 if(j==1)f[i][j]=f[i-1][j]^1; 91 else f[i][j]=f[i][j-1]^1; 92 } 93 int sum=0; 94 for(int i=1;i<=n;i++) 95 for(int j=1;j<=m;j++) 96 { 97 sum+=A[i][j]+B[i][j]; 98 if(f[i][j]==1) 99 { 100 ins(st,d[i][j],A[i][j]); 101 ins(d[i][j],ed,B[i][j]); 102 } 103 else 104 { 105 ins(st,d[i][j],B[i][j]); 106 ins(d[i][j],ed,A[i][j]); 107 } 108 } 109 for(int i=1;i<=n;i++) 110 for(int j=1;j<=m;j++) 111 { 112 if(i-1>0)ins(d[i][j],d[i-1][j],C[i][j]+C[i-1][j]),sum+=C[i][j]; 113 if(i+1<=n)ins(d[i][j],d[i+1][j],C[i][j]+C[i+1][j]),sum+=C[i][j]; 114 if(j-1>0)ins(d[i][j],d[i][j-1],C[i][j]+C[i][j-1]),sum+=C[i][j]; 115 if(j+1<=m)ins(d[i][j],d[i][j+1],C[i][j]+C[i][j+1]),sum+=C[i][j]; 116 } 117 int ans=0; 118 while(bt_h())ans+=find_flow(st,999999999); 119 printf("%d\n",sum-ans); 120 return 0; 121 }