zoukankan      html  css  js  c++  java
  • [BZOJ5120] [2017国家集训队测试]无限之环

    Description

    曾经有一款流行的游戏,叫做InfinityLoop,先来简单的介绍一下这个游戏:

    游戏在一个n×m的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中点有接口

    ,所有水管的粗细都相同,所以如果两个相邻方格的公共边界的中点都有接头,那么可以看作这两个接头互相连接

    。水管有以下15种形状:

    img

    游戏开始时,棋盘中水管可能存在漏水的地方。

    形式化地:如果存在某个接头,没有和其它接头相连接,那么它就是一个漏水的地方。

    玩家可以进行一种操作:选定一个含有非直线型水管的方格,将其中的水管绕方格中心顺时针或逆时针旋转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
    

    Solution

    这是个什么鬼题嘛....

    思路不是很复杂,考虑网络流,每个点拆成五个,上下左右各一个,中间一个,设为(P_{x,0..4})

    对网格图进行黑白染色,对于黑点,源点(s)(P_{x,0})连边;对于白点,(P_{x,0})(t)连边,容量(+infty),费用(0)

    对于相邻的两个点,我们设黑点为发射点,白点为接收点,那么黑点另外四个点向相应的四周的点连边,比如说(u=(x,y),v=(x+1,y)),且(u)为黑点,那么我们可以连边(P_{u,2} o P_{v,4});若(u)为白点就连边(P_{v,4} o P_{u,2})。其中容量为(1),费用为(0)

    那么一个基本的框架就构造完成了,现在考虑如何旋转。

    • 首先,对于任意一个图形,不考虑旋转,那么中心点向对应点连边,容量(1),费用(0)
    • 若当前点为直线或者没有,直接不管就好了。
    • 设当前点的形状为(x),设(t=bit\_cnt(x))表示当前点有几个连出去的边,那么可以分情况讨论:
      • (t=1),向相邻方向连容量为(1),费用为(1)的边,向对面方向连费用(2)的边。
      • (t=2),对于每个方向向对面方向连费用为(1),容量为(1)的边。
      • (t=3),其实和(t=1)的情况差不多,相当于是反向连边,缺口那个方向相邻的向缺口连费用(1)的,相对的连费用为(2)的。
    • 其实质上就相当于是每次旋转都只会移动一个方向,顺着这个思路画画图连边就好了,这也是为啥题目要规定直线不能转,因为直线转了就会有两个方向的改变。

    然后这题就做完了,代码及其恶心...

    #include<bits/stdc++.h>
    using namespace std;
     
    void read(int &x) {
        x=0;int f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
     
    void print(int x) {
        if(x<0) putchar('-'),x=-x;
        if(!x) return ;print(x/10),putchar(x%10+48);
    }
    void write(int x) {if(!x) putchar('0');else print(x);putchar('
    ');}
    
    const int maxn = 2e5+10;
    const int inf = 1e9;
    
    int s,t,n,m,tot=1,cost,flow,sum;
    int dis[maxn],vis[maxn],head[maxn],mp[maxn];
    struct edge{int to,nxt,w,c;}e[maxn];
    
    // emmm
    
    int bfs() {
    	memset(dis,63,(t+1)*4);
    	memset(vis,0,(t+1)*4);
    	queue<int > q;q.push(s);dis[s]=0,vis[s]=1;
    	while(!q.empty()) {
    		int x=q.front();q.pop();vis[x]=0;
    		for(int i=head[x],v;i;i=e[i].nxt)
    			if(e[i].w>0&&dis[v=e[i].to]>dis[x]+e[i].c) {
    				dis[v]=dis[x]+e[i].c;
    				if(!vis[v]) q.push(v),vis[v]=1;
    			}
    	}return dis[t]<inf;
    }
    
    int dfs(int x,int f) {
    	vis[x]=1;
    	if(x==t) return cost+=f*dis[t],f;
    	int used=0;
    	for(int v,i=head[x];i;i=e[i].nxt)
    		if(e[i].w>0&&(!vis[v=e[i].to]||v==t)&&dis[v]==dis[x]+e[i].c) {
    			int d=dfs(e[i].to,min(f-used,e[i].w));
    			if(d>0) e[i].w-=d,e[i^1].w+=d,used+=d;
    			if(used==f) break;
    		}
    	return used;
    }
    
    int mcmf() {
    	while(bfs()) flow+=dfs(s,inf);return cost;
    }
    
    // ---------
    
    void add(int u,int v,int w,int c) {e[++tot]=(edge){v,head[u],w,c},head[u]=tot;}
    void ins(int u,int v,int w,int c,int bo) {if(!bo) swap(u,v);add(u,v,w,c),add(v,u,0,-c);}
    
    int p(int x,int y,int tt) {return (x-1)*m+y+tt*n*m;}
    
    const int dk[] = {0,2,1,4,3};
    
    int main() {
    	read(n),read(m);s=n*m*5+1,t=s+1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			if((i+j)&1) ins(s,p(i,j,0),inf,0,1);
    			else ins(p(i,j,0),t,inf,0,1);
    	for(int i=1,x;i<=n;i++)
    		for(int j=1;j<=m;j++) {
    			if((i+j)&1) {
    				if(i>1) ins(p(i,j,2),p(i-1,j,4),1,0,1);
    				if(j>1) ins(p(i,j,3),p(i,j-1,1),1,0,1);
    				if(i<n) ins(p(i,j,4),p(i+1,j,2),1,0,1);
    				if(j<m) ins(p(i,j,1),p(i,j+1,3),1,0,1);
    			}
    			read(x);int bo=(i+j)&1;
    			int bit=__builtin_popcount(x),tt=1;
    			sum+=bit;
    			if(!x) continue;
    			if(x==5||x==10) {
    				for(int k=1;k<=4;k++)
    					if(x>>(k-1)&1) ins(p(i,j,0),p(i,j,dk[k]),1,0,bo);
    				continue;
    			}
    			if(bit==1) {
    				if(x==1) tt=2;else if(x==8) tt=3;else if(x==4) tt=4;
    				for(int k=1;k<=4;k++)
    					ins(p(i,j,tt),p(i,j,(tt+k+3)%4+1),1,min(k,4-k),bo);
    			} else if(bit==2) {
    				int a=0,b;
    				for(int k=1;k<=4;k++)
    					if((x>>(k-1))&1)
    						{if(!a) a=dk[k];else b=dk[k];}
    				ins(p(i,j,a),p(i,j,(a+5)%4+1),1,1,bo);
    				ins(p(i,j,b),p(i,j,(b+5)%4+1),1,1,bo);
    			} else if(bit==3) {
    				int mid,l;
    				if(x==11) mid=2;
    				else if(x==13) mid=3;
    				else if(x==14) mid=4;
    				else mid=1;l=(mid+5)%4+1;
    				for(int k=1;k<=4;k++)
    					if((x>>(k-1))&1) {
    						ins(p(i,j,dk[k]),p(i,j,l),1,dk[k]==mid?2:1,bo);
    					}
    			}
    			for(int k=1;k<=4;k++)
    				if(x>>(k-1)&1) ins(p(i,j,0),p(i,j,dk[k]),1,0,bo);
    		}
    	
    	mcmf();
    	if((flow<<1)==sum) write(cost);else puts("-1");
    	return 0;
    }
    
  • 相关阅读:
    caffe:mac10.12安装caffe的步骤
    查找两个链表的共同子链表
    golang:1.并发编程之互斥锁、读写锁详解
    git问题汇总
    有用的技术工具
    maven 安装本地jar包到本地maven仓库
    win7下Hadoop学习 之 Cygwin下载、安装、配置
    简单目录备份脚本
    2021年01月28日微博热搜汇总
    2021年01月26日微博热搜汇总
  • 原文地址:https://www.cnblogs.com/hbyer/p/10485797.html
Copyright © 2011-2022 走看看