1. 程式人生 > >BZOJ 3144 切糕 最小割

BZOJ 3144 切糕 最小割

題意:

  一個矩陣,每個格子分配一個數,不同的數字,代價不同,要求相鄰格子數字差小等於d

  求最小代價。

 

分析:

  我猜肯定有人看題目就想到最小割了,然後一看題面理科否決了自己的這個想法……

  沒錯,就是最小割……

  你是否還記得,在第一篇網路流題解中,我們瞭解了網路流最重要的是“限制”二字。

  我們在這道題中,先把限制放寬,考慮在不限制編號差小於等於d的情況下,怎麼辦?

  我們俯視這個立方體,把每個位置的所有層的點由下到上連起來,變成P*Q個點串,底面上所有的點連源點,頂面上所有點連匯點,權值反應在邊上,求最小割即可。

  那我們現在有限制了,怎麼辦?

  一個思路是,我們想辦法強制如果相鄰兩個點割開的位置距離大於d,那就絕對不能割開這兩個點,所以我們有一個這樣的臉變方法:從當前點串的i號點向相鄰點串的i-d號點連一條正無窮的邊,這樣呢,當我們兩個相鄰的點串有割開距離大於d的時候,自然會有一條正無窮的邊讓我們割不開,於是跑最小割就可以合法了!

程式碼:

 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=100005,M=45,inf=0x3f3f3f3f
; 5 struct node{int y,z,nxt;}e[N*10]; 6 int n,m,k,D,S,T,c=1,f[M][M][M],h[N],d[N]; 7 int q[N],xx[4]={0,0,1,-1},yy[4]={1,-1,0,0}; 8 int p(int x,int y,int z){ 9 return z==0?0:(z-1)*n*m+(x-1)*m+y; 10 } void add(int x,int y,int z){ 11 e[++c]=(node){y,z,h[x]};h[x]=c; 12 e[++c]=(node){x,0,h[y]};h[y]=c;
13 } bool bfs(){ 14 int f=1,t=0;ms(d,-1); 15 q[++t]=S;d[S]=0; 16 while(f<=t){ 17 int x=q[f++]; 18 for(int i=h[x],y;i;i=e[i].nxt) 19 if(d[y=e[i].y]==-1&&e[i].z) 20 d[y]=d[x]+1,q[++t]=y; 21 } return (d[T]!=-1); 22 } int dfs(int x,int f){ 23 if(x==T) return f;int w,tmp=0; 24 for(int i=h[x],y;i;i=e[i].nxt) 25 if(d[y=e[i].y]==d[x]+1&&e[i].z){ 26 w=dfs(y,min(e[i].z,f-tmp)); 27 if(!w) d[y]=-1; 28 e[i].z-=w;e[i^1].z+=w; 29 tmp+=w;if(tmp==f) return f; 30 } return tmp; 31 } void dinic(){ 32 while(bfs()) tot+=dfs(S,inf); 33 } void build(){ 34 for(int i=1;i<=n;i++) 35 for(int j=1;j<=m;add(p(i,j,k),T,inf),j++) 36 for(int v=1;v<=k;v++){ 37 add(p(i,j,v-1),p(i,j,v),f[i][j][v]); 38 if(v>D) 39 for(int u=0;u<4;u++){ 40 int nx=i+xx[u],ny=j+yy[u]; 41 if(nx<1||ny<1||nx>n||ny>m) continue; 42 add(p(i,j,v),p(nx,ny,v-D),inf); 43 } 44 } 45 } 46 int main(){ 47 scanf("%d%d%d",&n,&m,&k); 48 S=0;T=n*m*k+1;scanf("%d",&D); 49 for(int i=1;i<=k;i++) 50 for(int j=1;j<=n;j++) 51 for(int v=1;v<=m;v++) 52 scanf("%d",&f[j][v][i]); 53 build();dinic(); 54 printf("%d\n",tot);return 0; 55 }
最小割