传送门:bzoj5120
题解
要求所有接头相连,实际上就是将边拆成入度和出度,要求满流。
将每个点拆成五个点,分别表示上下左右和中心点。按横纵坐标和奇偶进行黑白染色,源点 S S S向所有黑点的中心点连流量 + ∞ +\infty +∞,费用 0 0 0的边,所有白点中心点连流量 + ∞ +\infty +∞,费用 0 0 0的边。
每个点按照接头形状从中心点向对应方向点连流量 1 1 1,费用 0 0 0的边。点与点之间接头相连即为相邻点之间方向点连边。(按黑白定向即可)
可以发现只有 1 , 2 , 3 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;
}