题目
描述
给出一个(n*m)的网格,每个格子里的水管可能向四个方向都有接口;
游戏的目的是不能让水管漏水,即所有接口都有另一个接口与之相接;
你一步可以将一个格子中的水管旋转(90^ {circ}),规定直线型的接口不能旋转;
求最小的步数;
范围
(nm le 2000);
题解
-
对每个点拆成上右下左四个接口(令标号为0123),将格子黑白染色并将接口分别向源汇连边;
-
对每种形状的水管分类讨论,用 ((a,b,w)) 表示在内部连边,仅讨论黑格子,白格子类似:
- 0 型:(0,1,1)(0,3,1)(0,2,2)
- 01型:(0,2,1)(1,3,1)
- 012型:(0,3,1)(2,3,1)(1,3,2)
- 其他型:内部不需要连边
-
主要思想是讨论每种形状转来转去时候的代价;
#include<bits/stdc++.h> #define I(i,j,k) (k*n*m+(i-1)*m+j) #define inf 0x3f3f3f3f using namespace std; const int N=40010; int n,m,o,hd[N],S,T,que[N],head,tail,vis[N],dis[N]; struct Edge{int v,nt,f,w;}E[N<<2]; void adde(int u,int v,int c,int w){ E[o]=(Edge){v,hd[u],c,w};hd[u]=o++; E[o]=(Edge){u,hd[v],0,-w};hd[v]=o++; } void add(int i,int j,int k1,int k2,int w){ if((i+j)&1)adde(I(i,j,k1),I(i,j,k2),1,w); else adde(I(i,j,k2),I(i,j,k1),1,w); } bool spfa(){ for(int i=S;i<=T;++i)vis[i]=0,dis[i]=inf; head=0;tail=1;vis[que[0]=T]=1;dis[T]=0; while(head!=tail){ int u=que[head++];if(head==T)head=0; vis[u]=0; for(int i=hd[u];~i;i=E[i].nt)if(E[i^1].f){ int v=E[i].v; if(dis[v]>dis[u]+E[i^1].w){ dis[v]=dis[u]+E[i^1].w; if(!vis[v]){ vis[v]=1,que[tail++]=v; if(tail==T)tail=0; } } } } return dis[S]!=inf; } int dfs(int u,int F){ vis[u]=1; if(u==T||!F)return F; int flow=0,f; for(int i=hd[u];~i;i=E[i].nt){ int v=E[i].v; if(!vis[v]&&dis[u]==dis[v]+E[i].w&&E[i].f&&(f=dfs(v,min(E[i].f,F)))){ flow+=f;F-=f; E[i].f-=f;E[i^1].f+=f; if(!F)break; } } return flow; } int main(){ // freopen("infinityloop.in","r",stdin); // freopen("infinityloop.out","w",stdout); scanf("%d%d",&n,&m); S=0;T=n*m*4+1; for(int i=S;i<=T;++i)hd[i]=-1; int mxf=0; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)if((i+j)&1){ if(i!=1)adde(I(i,j,0),I(i-1,j,2),1,0); if(j!=1)adde(I(i,j,3),I(i,j-1,1),1,0); if(i!=n)adde(I(i,j,2),I(i+1,j,0),1,0); if(j!=m)adde(I(i,j,1),I(i,j+1,3),1,0); } int mx1=0,mx2=0; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ int x;scanf("%d",&x); if((i+j)&1)mx1+=__builtin_popcount(x); else mx2+=__builtin_popcount(x); for(int k=0;k<4;++k)if(x>>k&1){ if((i+j)&1)adde(S,I(i,j,k),1,0); else adde(I(i,j,k),T,1,0); } switch (x){ case 1:add(i,j,0,1,1);add(i,j,0,3,1);add(i,j,0,2,2);break; case 2:add(i,j,1,0,1);add(i,j,1,2,1);add(i,j,1,3,2);break; case 4:add(i,j,2,1,1);add(i,j,2,3,1);add(i,j,2,0,2);break; case 8:add(i,j,3,0,1);add(i,j,3,2,1);add(i,j,3,1,2);break; case 3:add(i,j,0,2,1);add(i,j,1,3,1);break; case 6:add(i,j,1,3,1);add(i,j,2,0,1);break; case 9:add(i,j,0,2,1);add(i,j,3,1,1);break; case 12:add(i,j,2,0,1);add(i,j,3,1,1);break; case 7:add(i,j,0,3,1);add(i,j,2,3,1);add(i,j,1,3,2);break; case 11:add(i,j,1,2,1);add(i,j,3,2,1);add(i,j,0,2,2);break; case 13:add(i,j,0,1,1);add(i,j,2,1,1);add(i,j,3,1,2);break; case 14:add(i,j,1,0,1);add(i,j,3,0,1);add(i,j,2,0,2);break; //case 0:break; //case 5:break; //case 10:break; //case 15:break; } } if(mx1!=mx2){puts("-1");return 0;} int flow=0,cost=0,f; while(spfa()){ do{ for(int i=S;i<=T;++i)vis[i]=0; f=dfs(S,inf); flow+=f; cost+=f*dis[S]; }while(vis[T]); } if(flow!=mx1)puts("-1"); else printf("%d ",cost); return 0; }