[BZOJ5120]无限之环

Description
曾经有一款流行的游戏,叫做InfinityLoop,先来简单的介绍一下这个游戏:
游戏在一个n×m的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中点有接口,所有水管的粗细都相同,所以如果两个相邻方格的公共边界的中点都有接头,那么可以看作这两个接头互相连接。水管有以下15种形状:

游戏开始时,棋盘中水管可能存在漏水的地方。
形式化地:如果存在某个接头,没有和其它接头相连接,那么它就是一个漏水的地方。
玩家可以进行一种操作:选定一个含有非直线型水管的方格,将其中的水管绕方格中心顺时针或逆时针旋转90度。
直线型水管是指左图里中间一行的两种水管。
现给出一个初始局面,请问最少进行多少次操作可以使棋盘上不存在漏水的地方。

Input
第一行两个正整数n,m代表网格的大小。
接下来n行每行m数,每个数是[0,15]中的一个
你可以将其看作一个4位的二进制数,从低到高每一位分别代表初始局面中这个格子上、右、下、左方向上是否有水管接头。
特别地,如果这个数是000,则意味着这个位置没有水管。
比如3(0011(2))代表上和右有接头,也就是一个L型,而12(1100(2))代表下和左有接头,也就是将L型旋转180度。
n×m≤2000

Output
输出共一行,表示最少操作次数。如果无法达成目标,输出-1

Sample Input
2 3
3 14 12
3 11 12

Sample Output
2

HINT
样例1棋盘如下

旋转方法很显然,先将左上角虚线方格内的水管顺时针转90度
然后右下角虚线方格内的水管逆时针旋转90度,这样就使得水管封闭了

样例 2 为题目描述中的第一张图片,无法达成目标。

样例 3 为题目描述中的第二张图片,将除了中心方格以外的每个方格内的水管都转 180 度即可。


看题解之前一脸懵逼……看完题解后大骂自己是zz……

首先我们黑白染色,分成黑白两部分,S向白点连边,黑点向T连边

然后考虑触点的方向有4个,那么我们显然把每个点拆成5个点,上下左右各一个,外加自己这个中心点

然后我们就可以考虑连边,然后旋转有费用,我们就建图跑费用流,那么,怎么连边?或者说,如何控制边的流量和费用?

分类讨论?讨论旋转后每个触点的出边?恭喜你,你可能会讨论到死去活来,甚至最后会TLE

其实我们已经拆完点了,那么我们只需要在内部讨论费用问题即可,根本不需要考虑旋转后触点的出边

第一种:单触点

这个好办,它原本指向哪个方向,我们让中心点连向该方向的小点,流量为1,费用为0;至于旋转,我们只需要让小点连向其他小点即可,流量为\(\infty\),费用分别为1,2,1(蛤?你问我哪个是1,哪个是2?这种事自己画个图判断去吧,反正不难)

第二种:双触点

直线型直接pass吧(不知道为什么赶快看题去,这东西我整半天没整明白咋连费用,最后发现题目不让旋转)

拐角型的话,我们假定触点在上右两侧,那么我们中心点就连出两条流量为1,费用为0的边出来;然后考虑旋转,就让上侧小点向下侧小点连一条流量为\(\infty\),费用为1的边,右向左也如此。然后你稍作模拟,发现不管拐角型转成啥样,费用都是对的

第三种:三触点

其实和单触点类似,不过在连出三条费用为0的边后,直接连接的三个小点分别向空出来的小点连一条流量为\(\infty\),费用分别为1,2,1的边(说了和单触点类似,所以自己画图稍微判下吧)

第四种:全触点

那就全连边呗,都是费用为0的

然后图建完了,直接上最小费用最大流即可

然后这是EK费用流和Dinic费用流的区别……不说了,以后都用Dinic了……

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
#define lowbit(x) ((x)&-(x))
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
	int x=0,f=1; char ch=gc();
	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline int read(){
	int x=0,f=1; char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x<0)	putchar('-'),x=-x;
	if (x>9)	print(x/10);
	putchar(x%10+'0');
}
const int N=1e4+2,M=4e4;
const int dx[4]={-1,0,1,0};
const int dy[4]={0,1,0,-1};
int pre[M+10],now[N+10],child[M+10],val[M+10],cost[M+10];
int dis[N+10],deep[N+10];
int n,m,S,T,All,tot=1;
void join(int x,int y,int z,int v){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z,cost[tot]=v;}
void insert(int x,int y,int z,int v,bool flag=1){
	if (!flag)	swap(x,y);
	join(x,y,z,v),join(y,x,0,-v);
}
int G(int x,int y){return (x-1)*m+y;}
bool in_map(int x,int y){return x>0&&x<=n&&y>0&&y<=m;}
bool SPFA(){
	memset(dis,63,sizeof(dis));
	memset(deep,255,sizeof(deep));
	static int h[N+10];
	static bool vis[N+10];
	int head=0,tail=1;
	h[1]=S,vis[S]=1,dis[S]=deep[S]=0;
	while (head!=tail){
		if (++head>N)	head=1;
		int Now=h[head];
		for (int p=now[Now],son=child[p];p;p=pre[p],son=child[p]){
			if (val[p]>0&&dis[son]>dis[Now]+cost[p]){
				dis[son]=dis[Now]+cost[p];
				deep[son]=deep[Now]+1;
				if (!vis[son]){
					if (++tail>N)	tail=1;
					vis[h[tail]=son]=1;
				}
			}
		}
		vis[Now]=0;
	}
	return ~deep[T];
}
int dfs(int x,int v){
	if (x==T)	return v;
	int res=0;
	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
		if (val[p]>0&&deep[son]>deep[x]&&dis[son]==dis[x]+cost[p]){
			int k=dfs(son,min(v,val[p]));
			res+=k,v-=k;
			val[p]-=k,val[p^1]+=k;
			if (!v)	break;
		}
	}
	if (!res)	deep[x]=-1;
	return res;
}
int MCMF(int Ans_Flow){
	int Max_Flow=0,Min_cost=0;
	while (SPFA()){
		int res=dfs(S,inf);
		Max_Flow+=res;
		Min_cost+=res*dis[T];
	}
	return Max_Flow==Ans_Flow?Min_cost:-1;
}
int main(){
	static int g[16]; g[0]=0;
	for (int i=1;i<16;i++)	g[i]=g[i-lowbit(i)]+1;
	n=read(),m=read(),All=n*m,S=All*5+1,T=S+1;
	int Ans_Flow1=0,Ans_Flow2=0;
	for (int i=1;i<=n;i++){
		for (int j=1;j<=m;j++){
			int x=read();
			if ((i+j)&1){
				insert(S,G(i,j),inf,0);
				Ans_Flow1+=g[x];
				for (int k=0;k<4;k++){
					int tx=i+dx[k],ty=j+dy[k];
					if (!in_map(tx,ty))	continue;
					insert(All*(k+1)+G(i,j),All*((k+2)%4+1)+G(tx,ty),1,0);
				}
			}else{
				insert(G(i,j),T,inf,0);
				Ans_Flow2+=g[x];
			}
			if (!g[x])	continue;
			if (g[x]==1){
				for (int k=0;k<4;k++){
					if (x>>k&1){
						insert(G(i,j),All*(k+1)+G(i,j),1,0,(i+j)&1);
						insert(All*(k+1)+G(i,j),All*((k+1)%4+1)+G(i,j),inf,1,(i+j)&1);
						insert(All*(k+1)+G(i,j),All*((k+2)%4+1)+G(i,j),inf,2,(i+j)&1);
						insert(All*(k+1)+G(i,j),All*((k+3)%4+1)+G(i,j),inf,1,(i+j)&1);
						break;
					}
				}
			}
			if (g[x]==2){
				int ID_1=-1,ID_2=-1;
				for (int k=0;k<4;k++)	if (x>>k&1)	!~ID_1?ID_1=k:ID_2=k;
				insert(G(i,j),All*(ID_1+1)+G(i,j),1,0,(i+j)&1);
				insert(G(i,j),All*(ID_2+1)+G(i,j),1,0,(i+j)&1);
				if (ID_2-ID_1==2)	continue;
				insert(All*(ID_1+1)+G(i,j),All*((ID_1+2)%4+1)+G(i,j),inf,1,(i+j)&1);
				insert(All*(ID_2+1)+G(i,j),All*((ID_2+2)%4+1)+G(i,j),inf,1,(i+j)&1);
			}
			if (g[x]==3){
				for (int k=0;k<4;k++){
					if (!(x>>k&1)){
						insert(G(i,j),All*((k+1)%4+1)+G(i,j),1,0,(i+j)&1);
						insert(G(i,j),All*((k+2)%4+1)+G(i,j),1,0,(i+j)&1);
						insert(G(i,j),All*((k+3)%4+1)+G(i,j),1,0,(i+j)&1);
						insert(All*((k+1)%4+1)+G(i,j),All*(k+1)+G(i,j),inf,1,(i+j)&1);
						insert(All*((k+2)%4+1)+G(i,j),All*(k+1)+G(i,j),inf,2,(i+j)&1);
						insert(All*((k+3)%4+1)+G(i,j),All*(k+1)+G(i,j),inf,1,(i+j)&1);
						break;
					}
				}
			}
			if (g[x]==4)
				for (int k=0;k<4;k++)
					insert(G(i,j),All*(k+1)+G(i,j),inf,0,(i+j)&1);
		}
	}
	int Ans_Flow=max(Ans_Flow1,Ans_Flow2);
	printf("%d\n",MCMF(Ans_Flow));
	return 0;
}
posted @ 2019-02-17 21:53  Wolfycz  阅读(297)  评论(0编辑  收藏  举报