1. 程式人生 > >【BZOJ】5120: [2017國家集訓隊測試]無限之環-最小費用流最大流

【BZOJ】5120: [2017國家集訓隊測試]無限之環-最小費用流最大流

傳送門:bzoj5120


題解

要求所有接頭相連,實際上就是將邊拆成入度和出度,要求滿流。

將每個點拆成五個點,分別表示上下左右和中心點。按橫縱座標和奇偶進行黑白染色,源點 S S 向所有黑點的中心點連流量 +

+\infty ,費用 0 0 的邊,所有白點中心點連流量 + +\infty ,費用 0
0
的邊。

每個點按照接頭形狀從中心點向對應方向點連流量 1 1 ,費用 0 0 的邊。點與點之間接頭相連即為相鄰點之間方向點連邊。(按黑白定向即可)

可以發現只有 1 , 2 , 3 1,2,3 個接頭的點旋轉之後是有效的,旋轉即為從原方向點向旋轉後連相應費用的邊,分類討論即可。


程式碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<queue>
#define rc(x) x+(lim<<2)
#define up(x) x+dr*lim
#define rt(x) x+((dr+1)&3)*lim
#define dn(x) x+((dr+2)&3)*lim
#define lf(x) x+((dr+3)&3)*lim
using namespace std;
const int N=2e4+10,M=1e6+10,inf=0x7f7f7f7f;

int n,m,sumflow,dr,lim,S,T,cst,oe;
int head[N],nxt[M],to[M],w[M],cc[M],tot=1;
int dis[N],vs[N],tim;bool inq[N];

inline void lk(int u,int v,int vv,int vc)
{
	if(oe) swap(u,v);
	to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;cc[tot]=vc;
	to[++tot]=u;nxt[tot]=head[v];head[v]=tot;w[tot]=0;cc[tot]=-vc;
}

deque<int>que;
inline bool spfa()
{
	memset(dis,0x7f,(T+1)<<2);int i,j,x;
	dis[T]=0;que.push_back(T);inq[T]=true;
	for(;!que.empty();){
		x=que.front();que.pop_front();
		for(i=head[x];i;i=nxt[i]){
			j=to[i];if((!w[i^1])||(dis[j]<=dis[x]-cc[i])) continue;
			dis[j]=dis[x]-cc[i];if(inq[j]) continue;
			if(que.empty() || dis[que.front()]<dis[j]) que.push_back(j);
			else que.push_front(j);inq[j]=true;
		}
		inq[x]=false;
	}
	return dis[S]<inf;
}

int dfs(int x,int f)
{
	vs[x]=tim;if(x==T) return f;
	int i,j,res,ss=0;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(vs[j]==tim || (!w[i]) || dis[j]!=dis[x]-cc[i]) continue;
		res=dfs(j,min(f-ss,w[i]));if(!res) continue;
		w[i]-=res;w[i^1]+=res;ss+=res;cst+=cc[i]*res;if(ss==f) return ss;
	}
	return ss;
}

int main(){
	int i,j,k,x,bs=0,ct,res=0;
	scanf("%d%d",&n,&m);
	lim=n*m;S=lim*5+1;T=S+1;
	for(i=1;i<=n;++i)
	  for(j=1;j<=m;++j){
	  	++bs;scanf("%d",&x);dr=oe=0;ct=rc(bs);
	  	((i+j)&1)?lk(S,ct,inf,0):lk(ct,T,inf,0);
	  	oe=(i+j)&1;
	  	if(i>1) lk(dn(bs-m),up(bs),1,0);
	  	if(j>1) lk(rt(bs-1),lf(bs),1,0);
	  	for(k=0;k<4;++k) if((x>>k)&1)
	  		lk(bs+k*lim,ct,1,0),sumflow++;
	  	switch(x){
	  		case 8:++dr;
	  		case 4:++dr;
	  		case 2:++dr;
	  		case 1:
	  			lk(rt(bs),up(bs),1,1);
	  			lk(lf(bs),up(bs),1,1);
	  			lk(dn(bs),up(bs),1,2);
	  			break;
	  		case 9:++dr;
	  		case 12:++dr;
	  		case 6:++dr;
	  		case 3:
	  			lk(dn(bs),up(bs),1,1);
	  			lk(lf(bs),rt(bs),1,1);
	  			break;
	  		case 13:++dr;
	  		case 14:++dr;
	  		case 7:++dr;
	  		case 11:
	  			lk(dn(bs),lf(bs),1,1);
	  			lk(dn(bs),rt(bs),1,1);
	  			lk(dn(bs),up(bs),1,2);
	  			break;
	  	}
	  }
	for(;spfa();){
		for(vs[T]=tim;vs[T]==tim;){
			tim++;res+=dfs(S,inf);
		}
	}
	if((res<<1)!=sumflow) cst=-1;
	printf("%d",cst);
	return 0;
}