1. 程式人生 > >BZOJ5120無限之環-費用流

BZOJ5120無限之環-費用流

傳送門
題意:看原題吧,想不出比原題更好的描述了- -
Solution:
一開始思考過用網路流,但是想不出如何建圖,最後還是去看了題解QwQ,建圖思路很妙啊,我們先把每個點拆成四個小點,分別對應上,下,左,右,然後對應每種水管在點內分別建圖(細節大家可以結合程式碼思考一下),由於這是一個二分圖(拆點之前),所以說我們對每個點進行黑白染色,這樣便可以確定每個點是連向T還是從S連過來,點與點之間都要建流量為1,費用為0的邊,最後跑費用流即可,注意這裡我們的流量表示的是“最大能匹配到的水管數量”,同時注意我們連邊的順序。

程式碼:

#include<cstdio>
#include<iostream>
using namespace std; const int NM=2010; const int N=8010; struct edg{ int flow,v,to,next; }e[20*N]; int tot=0; int head[N]; int n,m,size=1,S,T; int id[NM][NM][4]; int kx[4]={-1,0,1,0}; int ky[4]={0,1,0,-1}; int pre[N],prv[N]; bool vis[N]; int dis[N]; int q[N*N]; void adde(int i,int j,int v,int f) { size++;e[size].to=j;e[size].next
=head[i];e[size].flow=f;e[size].v=v;head[i]=size; size++;e[size].to=i;e[size].next=head[j];e[size].flow=0;e[size].v=-v;head[j]=size; } void add(int i,int j,int v,int f,int b) { if (b) adde(i,j,v,f); else if (i==S) adde(j,T,v,f); else adde(j,i,v,f); } void work(int i,int j,int type,int
pos,int b) { if (type==1) { add(S,id[i][j][pos],0,1,b); add(id[i][j][pos],id[i][j][(pos+1)%4],1,1,b); add(id[i][j][pos],id[i][j][(pos+2)%4],2,1,b); add(id[i][j][pos],id[i][j][(pos+3)%4],1,1,b); } else if (type==2) { add(S,id[i][j][pos],0,1,b); add(S,id[i][j][(pos+1)%4],0,1,b); add(id[i][j][pos],id[i][j][(pos+2)%4],1,1,b); add(id[i][j][(pos+1)%4],id[i][j][(pos+3)%4],1,1,b); } else if (type==3) { add(S,id[i][j][pos],0,1,b); add(S,id[i][j][(pos+1)%4],0,1,b); add(S,id[i][j][(pos+2)%4],0,1,b); add(id[i][j][pos],id[i][j][(pos+3)%4],1,1,b); add(id[i][j][(pos+1)%4],id[i][j][(pos+3)%4],2,1,b); add(id[i][j][(pos+2)%4],id[i][j][(pos+3)%4],1,1,b); } else if (type==4) { add(S,id[i][j][pos],0,1,b); add(S,id[i][j][(pos+2)%4],0,1,b); } else { add(S,id[i][j][pos],0,1,b); add(S,id[i][j][(pos+1)%4],0,1,b); add(S,id[i][j][(pos+2)%4],0,1,b); add(S,id[i][j][(pos+3)%4],0,1,b); } } bool SPFA() { for (int i=S;i<=T;i++) vis[i]=0,dis[i]=1e9; int h=0,t=0; int x,y; q[++t]=S; vis[S]=1; dis[S]=0; while (h<t) { x=q[++h];vis[x]=0; for (int i=head[x];i;i=e[i].next) { y=e[i].to; if (e[i].flow&&dis[y]>dis[x]+e[i].v) { dis[y]=dis[x]+e[i].v; pre[y]=x; prv[y]=i; if (!vis[y]) { q[++t]=y; vis[y]=1; } } } } return (dis[T]!=1e9); } int main() { scanf("%d%d",&n,&m); S=0;T=n*m*4+1; int sum=0; for (int x,i=1;i<=n;i++) for (int j=1;j<=m;j++) { scanf("%d",&x); for (int k=0;k<4;k++) id[i][j][k]=++tot; if (x==1) work(i,j,1,0,(i+j)&1),sum++; else if (x==2) work(i,j,1,1,(i+j)&1),sum++; else if (x==3) work(i,j,2,0,(i+j)&1),sum+=2; else if (x==4) work(i,j,1,2,(i+j)&1),sum++; else if (x==5) work(i,j,4,0,(i+j)&1),sum+=2; else if (x==6) work(i,j,2,1,(i+j)&1),sum+=2; else if (x==7) work(i,j,3,0,(i+j)&1),sum+=3; else if (x==8) work(i,j,1,3,(i+j)&1),sum+=1; else if (x==9) work(i,j,2,3,(i+j)&1),sum+=2; else if (x==10) work(i,j,4,1,(i+j)&1),sum+=2; else if (x==11) work(i,j,3,3,(i+j)&1),sum+=3; else if (x==12) work(i,j,2,2,(i+j)&1),sum+=2; else if (x==13) work(i,j,3,2,(i+j)&1),sum+=3; else if (x==14) work(i,j,3,1,(i+j)&1),sum+=3; else if (x==15) work(i,j,5,0,(i+j)&1),sum+=4; } for (int i=1,j,_x,_y;i<=n;i++) for (j=1;j<=m;j++) if ((i+j)&1) for (int k=0;k<4;k++) { _x=i+kx[k],_y=j+ky[k]; if (_x>=1&&_x<=n&&_y>=1&&_y<=m) adde(id[i][j][k],id[_x][_y][(k+2)%4],0,1); } int ansv=0,ansf=0; while (SPFA()) { int F=1e9; for (int i=T;i!=S;i=pre[i]) F=min(F,e[prv[i]].flow); ansv+=F*dis[T]; ansf+=F; for (int i=T;i!=S;i=pre[i]) e[prv[i]].flow-=F,e[prv[i]^1].flow+=F; } printf("%d",(ansf==sum/2)?ansv:-1); }