[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;
}
[BZOJ5120]无限之环的更多相关文章
- BZOJ5120 无限之环(费用流)
方案合法相当于要求接口之间配对,黑白染色一波,考虑网络流.有一个很奇怪的限制是不能旋转直线型水管,考虑非直线型水管有什么特殊性,可以发现其接口都是连续的.那么对于旋转水管,可以看做是把顺/逆时针方向上 ...
- Loj #2321. 「清华集训 2017」无限之环
Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...
- Noip模拟63 2021.9.27(考场惊现无限之环)
T1 电压机制 把题目转化为找那些边只被奇数环包含. 这样的话直接$dfs$生成一棵树,给每个点附上一个深度,根据其他的非树边都是返祖边 可以算出环内边的数量$dep[x]-dep[y]+1$,然后判 ...
- BZOJ5120 [2017国家集训队测试]无限之环 费用流
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ5120 题意概括 原题挺简略的. 题解 本题好难. 听了任轩笛大佬<国家队神犇>的讲课才 ...
- [BZOJ5120] [2017国家集训队测试]无限之环
Description 曾经有一款流行的游戏,叫做InfinityLoop,先来简单的介绍一下这个游戏: 游戏在一个n×m的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中 ...
- 洛谷P4003 无限之环(infinityloop)(网络流,费用流)
洛谷题目传送门 题目 题目描述 曾经有一款流行的游戏,叫做 Infinity Loop,先来简单的介绍一下这个游戏: 游戏在一个 n ∗ m 的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在格 ...
- 【uoj336】【清华集训2017】无限之环
题目 描述 给出一个\(n*m\)的网格,每个格子里的水管可能向四个方向都有接口: 游戏的目的是不能让水管漏水,即所有接口都有另一个接口与之相接: 你一步可以将一个格子中的水管旋转\(90 ...
- BZOJ.5120.[清华集训2017]无限之环(费用流zkw 黑白染色)
题目链接 LOJ 洛谷 容易想到最小费用最大流分配度数. 因为水管形态固定,每个点还是要拆成4个点,分别当前格子表示向上右下左方向. 然后能比较容易地得到每种状态向其它状态转移的费用(比如原向上的可以 ...
- LOJ2321. 「清华集训 2017」无限之环【费用流】
LINK 很好的一道网络里题 首先想插头DP的还是出门左转10分代码吧 然后考虑怎么网络流 首先要保证没有漏水 也就是说每个接口一定要有对应的接口 那么发现每个点只有可能和上下左右四个点产生联通关系 ...
随机推荐
- java的多态以及重载,重写,前期绑定,后期绑定
多态的定义: 一个类实例的相同方法在不同情形有不同表现形式.多态机制使具有不同内部结构的对象可以共享相同的外部接口.这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通 ...
- linux 文件记录锁详解
一: linux记录锁更恰当的称呼应该是范围锁,它是对文件某个范围的锁定. 关于记录锁的功能就是fcntl提供的第五个功能,具体使用如下: int fcntl(int fd, int cmd, str ...
- 浅谈JavaScript的事件(事件流)
事件流描述的是从页面中接收事件的顺序.IE的事件流失事件冒泡,而Netspace的事件流失事件捕获. 事件冒泡 IE的事件流叫事件冒泡,即事件开始时,由具体的元素(文档中嵌套层次最深的节点)接收,然 ...
- C/C++语言中的位运算
在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作. 一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率.C语 ...
- 组合式+迭代式+链式 MapReduce
1.迭代式mapreduce 一些复杂的任务难以用一次mapreduce处理完成,需要多次mapreduce才能完成任务,例如Pagrank,Kmeans算法都需要多次的迭代,关于mapreduce迭 ...
- About "self"
Class method can't refer derectly to instance variables. Within the body of a class method, self re ...
- POJ3278 Catch That Cow —— BFS
题目链接:http://poj.org/problem?id=3278 Catch That Cow Time Limit: 2000MS Memory Limit: 65536K Total S ...
- 基于Vuejs的搜索匹配功能
最近一直在看vue,查了很多资料,看了很多文档和博客,大概半知半解了,然后利用所理解的知识写了一个简单的搜索匹配功能. 大概长这个样子: <!DOCTYPE html> <htm ...
- java24点算法
输入任意的四个数,求出所有能得到二十四点的算式,不过我是菜鸟,可能性能方面不好,希望各位多多指教1. [代码][Java]代码 import java.util.ArrayList;impo ...
- poj 1724 ROADS 解题报告
题目链接:http://poj.org/problem?id=1724 题目意思:给出一个含有N个点(编号从1~N).R条边的有向图.Bob 有 K 那么多的金钱,需要找一条从顶点1到顶点N的路径(每 ...