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;
}

[BZOJ5120]无限之环的更多相关文章

  1. BZOJ5120 无限之环(费用流)

    方案合法相当于要求接口之间配对,黑白染色一波,考虑网络流.有一个很奇怪的限制是不能旋转直线型水管,考虑非直线型水管有什么特殊性,可以发现其接口都是连续的.那么对于旋转水管,可以看做是把顺/逆时针方向上 ...

  2. Loj #2321. 「清华集训 2017」无限之环

    Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...

  3. Noip模拟63 2021.9.27(考场惊现无限之环)

    T1 电压机制 把题目转化为找那些边只被奇数环包含. 这样的话直接$dfs$生成一棵树,给每个点附上一个深度,根据其他的非树边都是返祖边 可以算出环内边的数量$dep[x]-dep[y]+1$,然后判 ...

  4. BZOJ5120 [2017国家集训队测试]无限之环 费用流

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ5120 题意概括 原题挺简略的. 题解 本题好难. 听了任轩笛大佬<国家队神犇>的讲课才 ...

  5. [BZOJ5120] [2017国家集训队测试]无限之环

    Description 曾经有一款流行的游戏,叫做InfinityLoop,先来简单的介绍一下这个游戏: 游戏在一个n×m的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中 ...

  6. 洛谷P4003 无限之环(infinityloop)(网络流,费用流)

    洛谷题目传送门 题目 题目描述 曾经有一款流行的游戏,叫做 Infinity Loop,先来简单的介绍一下这个游戏: 游戏在一个 n ∗ m 的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在格 ...

  7. 【uoj336】【清华集训2017】无限之环

    题目 描述 ​ 给出一个\(n*m\)的网格,每个格子里的水管可能向四个方向都有接口: ​ 游戏的目的是不能让水管漏水,即所有接口都有另一个接口与之相接: ​ 你一步可以将一个格子中的水管旋转\(90 ...

  8. BZOJ.5120.[清华集训2017]无限之环(费用流zkw 黑白染色)

    题目链接 LOJ 洛谷 容易想到最小费用最大流分配度数. 因为水管形态固定,每个点还是要拆成4个点,分别当前格子表示向上右下左方向. 然后能比较容易地得到每种状态向其它状态转移的费用(比如原向上的可以 ...

  9. LOJ2321. 「清华集训 2017」无限之环【费用流】

    LINK 很好的一道网络里题 首先想插头DP的还是出门左转10分代码吧 然后考虑怎么网络流 首先要保证没有漏水 也就是说每个接口一定要有对应的接口 那么发现每个点只有可能和上下左右四个点产生联通关系 ...

随机推荐

  1. 阿里云安装nginx 启动失败的原因。

    阿里云编译安装nginx服务器后启动一直报下面错误. 百度了一圈,看到一个说要先关掉apache服务,感觉这个好像是对的,立马做了下面操作. 果然把nginx起了起来. 从这边才知道apache和ng ...

  2. Facebook内部高效工作指南

    文章来源: TopDigital http://news.ittime.com.cn/usershow/main?userid=2826 [IT时代网.IT时代周刊编者按]每一个人工作中都会遇到力不从 ...

  3. webdriver.close() quit() 批量kill进程 内存耗尽的解决办法

    问题现象: shell窗口卡,换IP的登录窗,不开: 猜测: 内存耗尽 spider_url,py driver = webdriver.PhantomJS( executable_path='/us ...

  4. 简说 call() 、apply() 、bind()

    对于这三个方法,我想一部分人还是比较陌生的. 所以今天来个简单的介绍~ 我们可以将call()和apply()看作是某个对象的方法,通过调用方法的形式来间接调用函数.call()和apply()的第一 ...

  5. luogu2398 SUM GCD

    题目大意:求sum i(1->n) (sum j(1->n) (gcd(i,j))). 对于每对(i,j)都来一次gcd很慢,但是我们知道,一个约数i在1~n范围内是n/i个数的约数.gc ...

  6. jquery和CSS3带倒影的3D万花筒旋转动画特效效果演示

    <!DOCTYPE html> <html> <head> <title></title> <meta charset='utf-8' ...

  7. PrintWrite

    向文本输出流打印对象的格式化表示形式.此类实现在 PrintStream 中的所有 print 方法.它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入. 与 Print ...

  8. Fastreport生成WEB报表

    开发WEB应用系统通常都会遇到报表打印问题.简单应用可利用IE的页面打印功能,利用HTML标签控制格式来实现.但复杂的业务型应用系统,报表不仅是组成应用的 重要部分,还常常是相当复杂的.现在很多应用系 ...

  9. Android font

    ╔════╦════════════════════════════╦═════════════════════════════╗ ║ ║ FONT FAMILY ║ TTF FILE ║ ╠════ ...

  10. 创建Android本地repo

    /**************************************************************************** * 创建Android本地repo * 说明 ...