1. 程式人生 > >BZOJ 3996 線性代數 最小割

BZOJ 3996 線性代數 最小割

題意:

  給出一個N*N的矩陣B和一個1*N的矩陣C。求出一個1*N的01矩陣A.使得

  D=(A*B-C)*A^T最大。其中A^T為A的轉置。輸出D

分析:

  這道題比較繞,我們需要看清題目中那個式子的本質。A*B的貢獻是正的,說明這是價值。C的貢獻是負的,說明這是代價。

  仔細理解這句話“只有ai和aj同時為1的時候,才對答案有bij的貢獻。使ai為1的代價為ci”

  我們現在是否能從題目中的式子中提煉出這個關係?如果能,請繼續

  為什麼說這題是最小割呢?因為,這裡有新的兩個字,當我們面臨這兩個字時,就要考慮最小割,那就是“取捨

  在這道題的意志中,我們需要選擇捨棄b帶來的相應價值,以此來避免付出代價,或者是為了獲得價值,而選擇捨棄而付出相應的代價。所以我們建圖為兩部分:

  左半部分,有n^2個點,(可以理解是我們抽象出的b陣列)從原點向(i,j)點連容量為bij的邊,右半部分有n個點,i號點向匯點連一條容量為ci的邊。

  點(i,j)右邊的點i和j分別連容量為inf的邊。

  這樣呢,我們的限制就是,要麼捨棄bij這個價值,要麼付出ci和cj的代價,對於每個點都是這樣,最小割,就是我們最少捨棄的貢獻。然後,我們求出b陣列的價值和,減去最小割就是我們最終獲得的最大貢獻。

程式碼:

 1 #include<bits/stdc++.h>
 2 #define ms(a,x) memset(a,x,sizeof(a)) 
 3
using namespace std;int tot=0; 4 const int N=1000005,M=505,inf=0x3f3f3f3f; 5 int S,T,n,m,k,h[N],c=1,q[N],d[N],b[M][M],C[M]; 6 struct node{int y,z,nxt;}e[N*4]; 7 void add(int x,int y,int z){ 8 e[++c]=(node){y,z,h[x]};h[x]=c; 9 e[++c]=(node){x,0,h[y]};h[y]=c; 10 } bool bfs(){ 11 int f=1,t=0;ms(d,-1
); 12 q[++t]=S;d[S]=0; 13 while(f<=t){ 14 int x=q[f++]; 15 for(int i=h[x],y;~i;i=e[i].nxt) 16 if(d[y=e[i].y]==-1&&e[i].z) 17 d[y]=d[x]+1,q[++t]=y; 18 } return (d[T]!=-1); 19 } int dfs(int x,int f){ 20 if(x==T) return f;int w,tmp=0; 21 for(int i=h[x],y;~i;i=e[i].nxt) 22 if(d[y=e[i].y]==d[x]+1&&e[i].z){ 23 w=dfs(y,min(e[i].z,f-tmp)); 24 if(!w) d[y]=-1;e[i].z-=w; 25 e[i^1].z+=w;tmp+=w; 26 if(tmp==f) return f; 27 } return tmp; 28 } void dinic(){ 29 while(bfs()) tot+=dfs(S,inf); 30 } int main(){ 31 scanf("%d",&n);S=0,T=n*n+n+10; 32 int sm=0,nm=0;ms(h,-1); 33 for(int i=1;i<=n;i++) 34 for(int j=1;j<=n;j++) 35 scanf("%d",&b[i][j]); 36 for(int i=1;i<=n;i++) 37 scanf("%d",&C[i]),add(i+n*n,T,C[i]); 38 for(int i=1;i<=n;i++) 39 for(int j=1;j<=n;j++) 40 sm+=b[i][j],add(S,++nm,b[i][j]), 41 add(nm,i+n*n,inf),add(nm,j+n*n,inf); 42 dinic();sm-=tot; 43 printf("%d\n",sm); 44 return 0; 45 }
最小割