【LOJ2321 「清华集训」2017无限之环】 题解

在经过 \(n\) 个小时的奋斗后,LsWn 终于 AC 了这个毒瘤题。

题面传送门

如果你更喜欢洛谷的话戳这里

我们先看一个正确可以流通的图 (加上了黑白相间染色,然后黄色方块代表这个格子的上方,红色代表下方,紫色是左,灰色是右)(最后一行的上下画反了……凑活凑活)。

黑白染色网络流一贯的套路,将黑点流向白点,源点流向黑点,白点流向汇点,然后根据拆点一贯的套路,内部连边。(指一个点拆成五个点,中间和上下左右)

显然,合理的方案就是所有的黑点和白点互相匹配,即有完备匹配,也就是满流,没有一滴水漏出来。从黑点流出的点都有去处。

这是一个合法方案。那么如果出了点小差错,需要旋转呢?

这就很谔谔,右下角没法连通。但是我们可以旋转。我们观察到,左旋一次,相当于把右插头换成左插头。于是连费用为 \(1\) 的边。

右旋一次,相当于把下插头换成上插头,于是连边。。旋转两次,走两次旋转一次,故不连边。

现在讨论连边情况:

  1. 类似于 ┌
    上面已经讲过,不再陈述。
  2. 类似于 Q
    如果左/右翻转,那么就等于插头向左/右换。翻转两次就是上下换,左右换。换两次的连边的费用是 \(2\)
  3. 类似于丨
    不处理,因为不能转。
  4. 类似于 T
    和 ┌ 比较相似。不过需要连上下边。
    5 类似于 十
    不处理,因为转了也没用。

然后总体 \(15\) 种情况还需一一分析……这就很毒瘤。这就放在代码中了。

一些细节:

  1. 不要重复建边!不要重复建边!不要重复建边!(否则你算出来的答案会小于标准答案)
  2. 毒瘤建边题的网络流真的不能码错!真的不能!
  3. hsh 函数一定要注意!
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e6+9,inf=0x3f3f3f3f3f3f3f3f;
int n,m,a[2009][2009],debugger;

struct edge{int to,nxt,c,w;}e[N*2]; int hd[N],tot=1;
void add(int u,int v,int c,int w){e[++tot]=(edge){v,hd[u],c,w};hd[u]=tot;}
void addh(int u,int v,int c,int w){
	//cout<<u<<" "<<v<<" "<<c<<" "<<w<<endl;
	debugger+=w;
	add(u,v,c,w),add(v,u,0,-w);
}
void addr(int u,int v,int c,int w){addh(v,u,c,w);}

int s,t,flow,cost,tmp,ans;
int d[N]; bool in[N]; 
bool spfa(){
	queue<int>q; q.push(s); memset(d,0x3f,sizeof(d)); d[s]=0;
	while(!q.empty()){
		int u=q.front(); q.pop(); in[u]=0;
		for(int i=hd[u],v;i;i=e[i].nxt)
			if(e[i].c&&d[v=e[i].to]>d[u]+e[i].w){
				d[v]=d[u]+e[i].w;
				if(!in[v]) q.push(v),in[v]=1;
			}
	}
	return d[t]<inf;
}
int dinic(int u,int flow){
	int rest=flow; if(u==t) return flow; in[u]=1;
	for(int i=hd[u],v;i&&rest;i=e[i].nxt)
		if(!in[v=e[i].to]&&e[i].c&&d[v]==d[u]+e[i].w){
			int used=dinic(v,min(e[i].c,rest));
			if(!used) d[v]=-1;
			rest-=used; e[i].c-=used,e[i^1].c+=used,cost+=used*e[i].w; 
		}
	in[u]=0;
	return flow-rest;
}

// 0:中  1:上  2:右  3:下  4:左 
int hsh(int x,int y,int loc){return (x*m-m+y)+loc*m*n;}
bool ok(int x,int y){return x>0&&x<=n&&y>0&&y<=m;}
signed main(){
	scanf("%lld%lld",&n,&m); s=n*m*5+1,t=n*m*5+2;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%lld",&a[i][j]);

	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){
		if((i+j)%2){ //黑格子 
			addh(s,hsh(i,j,0),inf,0);
			if(ok(i-1,j))
				addh(hsh(i,j,1),hsh(i-1,j,3),1,0);
			if(ok(i,j+1))
				addh(hsh(i,j,2),hsh(i,j+1,4),1,0);
			if(ok(i+1,j))	
				addh(hsh(i,j,3),hsh(i+1,j,1),1,0);
			if(ok(i,j-1))
				addh(hsh(i,j,4),hsh(i,j-1,2),1,0);
			for(int k=1;k<=4;k++)
				if(a[i][j]&(1ll<<(k-1))){
					addh(hsh(i,j,0),hsh(i,j,k),1,0);
					ans++;
				}
			if(a[i][j]==15);	 //全 
			else if(a[i][j]==1){ //上 
				addh(hsh(i,j,1),hsh(i,j,2),1,1);
				addh(hsh(i,j,1),hsh(i,j,4),1,1);
				addh(hsh(i,j,1),hsh(i,j,3),1,2);
			}
			else if(a[i][j]==2){ //右
				addh(hsh(i,j,2),hsh(i,j,1),1,1);
				addh(hsh(i,j,2),hsh(i,j,3),1,1);
				addh(hsh(i,j,2),hsh(i,j,4),1,2);
			} 
			else if(a[i][j]==3){ //上右 
				addh(hsh(i,j,1),hsh(i,j,3),1,1);
				addh(hsh(i,j,2),hsh(i,j,4),1,1);
			}
			else if(a[i][j]==4){ //下 
				addh(hsh(i,j,3),hsh(i,j,2),1,1);
				addh(hsh(i,j,3),hsh(i,j,4),1,1);
				addh(hsh(i,j,3),hsh(i,j,1),1,2);
			} 
			else if(a[i][j]==5); //上下 
			else if(a[i][j]==6){ //右下 
				addh(hsh(i,j,3),hsh(i,j,1),1,1);
				addh(hsh(i,j,2),hsh(i,j,4),1,1);
			}
			else if(a[i][j]==7){ //上右下 
				addh(hsh(i,j,1),hsh(i,j,4),1,1);
				addh(hsh(i,j,3),hsh(i,j,4),1,1);
				addh(hsh(i,j,2),hsh(i,j,4),1,2);
			}
			else if(a[i][j]==8){ //左 
				addh(hsh(i,j,4),hsh(i,j,1),1,1);
				addh(hsh(i,j,4),hsh(i,j,3),1,1);
				addh(hsh(i,j,4),hsh(i,j,2),1,2);
			}
			else if(a[i][j]==9){ //上左 
				addh(hsh(i,j,1),hsh(i,j,3),1,1);
				addh(hsh(i,j,4),hsh(i,j,2),1,1); 
			}
			else if(a[i][j]==10);//右左 
			else if(a[i][j]==11){//上右左 
				addh(hsh(i,j,2),hsh(i,j,3),1,1);
				addh(hsh(i,j,4),hsh(i,j,3),1,1);
				addh(hsh(i,j,1),hsh(i,j,3),1,2);
			}
			else if(a[i][j]==12){//下左 
				addh(hsh(i,j,3),hsh(i,j,1),1,1);
				addh(hsh(i,j,4),hsh(i,j,2),1,1); 
			}
			else if(a[i][j]==13){//上下左 
				addh(hsh(i,j,1),hsh(i,j,2),1,1);
				addh(hsh(i,j,3),hsh(i,j,2),1,1);
				addh(hsh(i,j,4),hsh(i,j,2),1,2);
			}
			else if(a[i][j]==14){//右下左 
				addh(hsh(i,j,2),hsh(i,j,1),1,1);
				addh(hsh(i,j,4),hsh(i,j,1),1,1);
				addh(hsh(i,j,3),hsh(i,j,1),1,2);
			}
		}
		else{
			addh(hsh(i,j,0),t,inf,0);
			for(int k=1;k<=4;k++)
				if(a[i][j]&(1<<(k-1))){
					addr(hsh(i,j,0),hsh(i,j,k),1,0);
					ans++;
				}
			if(a[i][j]==15);	 //全 
			else if(a[i][j]==1){ //上 
				addr(hsh(i,j,1),hsh(i,j,2),1,1);
				addr(hsh(i,j,1),hsh(i,j,4),1,1);
				addr(hsh(i,j,1),hsh(i,j,3),1,2);
			}
			else if(a[i][j]==2){ //右
				addr(hsh(i,j,2),hsh(i,j,1),1,1);
				addr(hsh(i,j,2),hsh(i,j,3),1,1);
				addr(hsh(i,j,2),hsh(i,j,4),1,2);
			} 
			else if(a[i][j]==3){ //上右 
				addr(hsh(i,j,1),hsh(i,j,3),1,1);
				addr(hsh(i,j,2),hsh(i,j,4),1,1);
			}
			else if(a[i][j]==4){ //下 
				addr(hsh(i,j,3),hsh(i,j,2),1,1);
				addr(hsh(i,j,3),hsh(i,j,4),1,1);
				addr(hsh(i,j,3),hsh(i,j,1),1,2);
			} 
			else if(a[i][j]==5); //上下 
			else if(a[i][j]==6){ //右下 
				addr(hsh(i,j,3),hsh(i,j,1),1,1);
				addr(hsh(i,j,2),hsh(i,j,4),1,1);
			}
			else if(a[i][j]==7){ //上右下 
				addr(hsh(i,j,1),hsh(i,j,4),1,1);
				addr(hsh(i,j,3),hsh(i,j,4),1,1);
				addr(hsh(i,j,2),hsh(i,j,4),1,2);
			}
			else if(a[i][j]==8){ //左 
				addr(hsh(i,j,4),hsh(i,j,1),1,1);
				addr(hsh(i,j,4),hsh(i,j,3),1,1);
				addr(hsh(i,j,4),hsh(i,j,2),1,2);
			}
			else if(a[i][j]==9){ //上左 
				addr(hsh(i,j,1),hsh(i,j,3),1,1);
				addr(hsh(i,j,4),hsh(i,j,2),1,1); 
			}
			else if(a[i][j]==10);//右左 
			else if(a[i][j]==11){//上右左 
				addr(hsh(i,j,2),hsh(i,j,3),1,1);
				addr(hsh(i,j,4),hsh(i,j,3),1,1);
				addr(hsh(i,j,1),hsh(i,j,3),1,2);
			}
			else if(a[i][j]==12){//下左 
				addr(hsh(i,j,3),hsh(i,j,1),1,1);
				addr(hsh(i,j,4),hsh(i,j,2),1,1); 
			}
			else if(a[i][j]==13){//上下左 
				addr(hsh(i,j,1),hsh(i,j,2),1,1);
				addr(hsh(i,j,3),hsh(i,j,2),1,1);
				addr(hsh(i,j,4),hsh(i,j,2),1,2);
			}
			else if(a[i][j]==14){//右下左 
				addr(hsh(i,j,2),hsh(i,j,1),1,1);
				addr(hsh(i,j,4),hsh(i,j,1),1,1);
				addr(hsh(i,j,3),hsh(i,j,1),1,2);
			}
		}
	}
	
	while(spfa()) while(tmp=dinic(s,inf)) flow+=tmp;
	if(flow*2==ans) printf("%lld\n",cost);
	else puts("-1");
	return 0;
}
原文地址:https://www.cnblogs.com/TetrisCandy/p/12781535.html