题目链接

LOJ

洛谷

容易想到最小费用最大流分配度数。

因为水管形态固定,每个点还是要拆成4个点,分别当前格子表示向上右下左方向。

然后能比较容易地得到每种状态向其它状态转移的费用(比如原向上的可以流到向下)。

注意比如向左向上的L,左连右,上连下,没有上连右(日常zz)。

可以看这的图

解决旋转的问题后,还要处理流量从哪里产生、结束。

因为是网格图,容易想到黑白染色。题目中"没有漏水水管"即格子的断头两两匹配,而匹配只发生在黑白格之间。so源点向所有白格子连边,所有黑格子向汇点连边。

因为匹配关系是确定的,所以即使相邻不一定有水管相连,匹配边还是要连的。

SPFA单路增广好慢啊,学一波多路增广。

可以,很快。

Update:我好像刚知道多路增广就是zkw费用流。。

朴素SPFA:

//7048kb	11328ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 200000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define OK(i,j) (1<=(i)&&(i)<=n&&1<=(j)&&(j)<=m)
const int N=1e4+5,M=N*30; int n,m,src,des,Enum,H[N],nxt[M],fr[M],to[M],cap[M],cost[M],pre[N];
std::queue<int> q;
char IN[MAXIN],*SS=IN,*TT=IN; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AE(int u,int v,int c,bool flag)
{
if(flag) std::swap(u,v);//黑→白 把边反向
to[++Enum]=v, fr[Enum]=u, nxt[Enum]=H[u], cost[Enum]=c, cap[Enum]=1, H[u]=Enum;
to[++Enum]=u, fr[Enum]=v, nxt[Enum]=H[v], cost[Enum]=-c, cap[Enum]=0, H[v]=Enum;
}
bool SPFA()
{
static int dis[N];
static bool inq[N];
memset(dis,0x3f,sizeof dis);
dis[src]=0, q.push(src);
while(!q.empty())
{
int x=q.front();
q.pop(), inq[x]=0;//!...
for(int v,i=H[x]; i; i=nxt[i])
if(cap[i] && dis[v=to[i]]>dis[x]+cost[i])
pre[v]=i, dis[v]=dis[x]+cost[i], !inq[v]&&(q.push(v),inq[v]=1);
}
return dis[des]<0x3f3f3f3f;
}
inline int Augment()
{
int res=0;
for(int i=des; i!=src; i=fr[pre[i]])
res+=cost[pre[i]], --cap[pre[i]], ++cap[pre[i]^1];
return res;
}
int MCMF(int &cost)
{
int res=0;
while(SPFA()) cost+=Augment(), ++res;
return res;
} int main()
{
n=read(),m=read(); int tot=0;
int id[n+1][m+1][4];
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
for(int k=0; k<4; ++k) id[i][j][k]=++tot;
Enum=1, src=0, des=++tot;
bool f; int flow=0;
for(int i=1; i<=n; ++i)
for(int j=1,s; j<=m; ++j)//0上 1右 2下 3左
{//左 下 右 上
s=read(), f=(i+j)&1;
int u=f?des:src,up=id[i][j][0],ri=id[i][j][1],down=id[i][j][2],le=id[i][j][3];
if(s&1) AE(u,up,0,f), flow+=f^1;
if(s&2) AE(u,ri,0,f), flow+=f^1;
if(s&4) AE(u,down,0,f), flow+=f^1;
if(s&8) AE(u,le,0,f), flow+=f^1;
// if(!f)
// for(int k=0; k<4; ++k)
// if(s>>k&1) AE(src,id[i][j][k],0,0), ++flow;
// else ;//else!
// else for(int k=0; k<4; ++k) if(s>>k&1) AE(id[i][j][k],des,0,0);
if(!f)
{
if(OK(i-1,j)) AE(up,id[i-1][j][2],0,0);
if(OK(i,j-1)) AE(le,id[i][j-1][1],0,0);
if(OK(i+1,j)) AE(down,id[i+1][j][0],0,0);
if(OK(i,j+1)) AE(ri,id[i][j+1][3],0,0);
}
switch(s)
{
case 0: break;
case 1: AE(up,le,1,f), AE(up,ri,1,f), AE(up,down,2,f); break;
case 2: AE(ri,up,1,f), AE(ri,down,1,f), AE(ri,le,2,f); break;
case 3: AE(up,down,1,f), AE(ri,le,1,f); break;
case 4: AE(down,le,1,f), AE(down,ri,1,f), AE(down,up,2,f); break;
case 5: break;
case 6: AE(ri,le,1,f), AE(down,up,1,f); break;
case 7: AE(up,le,1,f), AE(down,le,1,f), AE(ri,le,2,f); break;
case 8: AE(le,up,1,f), AE(le,down,1,f), AE(le,ri,2,f); break;
case 9: AE(le,ri,1,f), AE(up,down,1,f); break;
case 10: break;
case 11: AE(le,down,1,f), AE(ri,down,1,f), AE(up,down,2,f); break;
case 12: AE(le,ri,1,f), AE(down,up,1,f); break;
case 13: AE(up,ri,1,f), AE(down,ri,1,f), AE(le,ri,2,f); break;
case 14: AE(le,up,1,f), AE(ri,up,1,f), AE(down,up,2,f); break;
case 15: break;
}
}
int cost=0;
if(MCMF(cost)==flow) printf("%d\n",cost);
else puts("-1"); return 0;
}

多路增广:

//5872kb	184ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 200000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define OK(i,j) (1<=(i)&&(i)<=n&&1<=(j)&&(j)<=m)
const int N=1e4+5,M=N*30; int n,m,src,des,Enum,H[N],cur[N],nxt[M],to[M],cap[M],cost[M],dis[N],Cost;
std::queue<int> q;
bool vis[N];
char IN[MAXIN],*SS=IN,*TT=IN; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AE(int u,int v,int c,bool flag)
{
if(flag) std::swap(u,v);//黑→白 把边反向
to[++Enum]=v, nxt[Enum]=H[u], cost[Enum]=c, cap[Enum]=1, H[u]=Enum;
to[++Enum]=u, nxt[Enum]=H[v], cost[Enum]=-c, cap[Enum]=0, H[v]=Enum;
}
bool SPFA()
{
memset(vis,0,sizeof vis);
memset(dis,0x3f,sizeof dis);
dis[src]=0, q.push(src);
while(!q.empty())
{
int x=q.front();
q.pop(), vis[x]=0;
for(int v,i=H[x]; i; i=nxt[i])
if(cap[i] && dis[v=to[i]]>dis[x]+cost[i])
dis[v]=dis[x]+cost[i], !vis[v]&&(q.push(v),vis[v]=1);
}
return dis[des]<0x3f3f3f3f;
}
int DFS(int x/*int f*/)
{
if(x==des) return 1;
vis[x]=1;
for(int &i=cur[x]; i; i=nxt[i])
if(!vis[to[i]] && cap[i] && dis[to[i]]==dis[x]+cost[i])
if(DFS(to[i]))
return --cap[i], ++cap[i^1], Cost+=cost[i], 1;
return 0;
}
int MCMF()
{
int flow=0;
while(SPFA())
{
for(int i=src; i<=des; ++i) cur[i]=H[i];
while(DFS(src)) ++flow;
}
return flow;
} int main()
{
n=read(),m=read(); int tot=0;
int id[n+1][m+1][4];
for(int i=1; i<=n; ++i)
for(int j=1; j<=m; ++j)
for(int k=0; k<4; ++k) id[i][j][k]=++tot;
Enum=1, src=0, des=++tot;
bool f; int flow=0;
for(int i=1; i<=n; ++i)
for(int j=1,s; j<=m; ++j)//0上 1右 2下 3左
{//左 下 右 上
s=read(), f=(i+j)&1;
int u=f?des:src,up=id[i][j][0],ri=id[i][j][1],down=id[i][j][2],le=id[i][j][3];
if(s&1) AE(u,up,0,f), flow+=f^1;
if(s&2) AE(u,ri,0,f), flow+=f^1;
if(s&4) AE(u,down,0,f), flow+=f^1;
if(s&8) AE(u,le,0,f), flow+=f^1;
// if(!f)
// for(int k=0; k<4; ++k)
// if(s>>k&1) AE(src,id[i][j][k],0,0), ++flow;
// else ;//else!
// else for(int k=0; k<4; ++k) if(s>>k&1) AE(id[i][j][k],des,0,0);
if(!f)
{
if(OK(i-1,j)) AE(up,id[i-1][j][2],0,0);
if(OK(i,j-1)) AE(le,id[i][j-1][1],0,0);
if(OK(i+1,j)) AE(down,id[i+1][j][0],0,0);
if(OK(i,j+1)) AE(ri,id[i][j+1][3],0,0);
}
switch(s)
{
case 0: break;
case 1: AE(up,le,1,f), AE(up,ri,1,f), AE(up,down,2,f); break;
case 2: AE(ri,up,1,f), AE(ri,down,1,f), AE(ri,le,2,f); break;
case 3: AE(up,down,1,f), AE(ri,le,1,f); break;
case 4: AE(down,le,1,f), AE(down,ri,1,f), AE(down,up,2,f); break;
case 5: break;
case 6: AE(ri,le,1,f), AE(down,up,1,f); break;
case 7: AE(up,le,1,f), AE(down,le,1,f), AE(ri,le,2,f); break;
case 8: AE(le,up,1,f), AE(le,down,1,f), AE(le,ri,2,f); break;
case 9: AE(le,ri,1,f), AE(up,down,1,f); break;
case 10: break;
case 11: AE(le,down,1,f), AE(ri,down,1,f), AE(up,down,2,f); break;
case 12: AE(le,ri,1,f), AE(down,up,1,f); break;
case 13: AE(up,ri,1,f), AE(down,ri,1,f), AE(le,ri,2,f); break;
case 14: AE(le,up,1,f), AE(ri,up,1,f), AE(down,up,2,f); break;
case 15: break;
}
}
if(MCMF()==flow) printf("%d\n",Cost);
else puts("-1"); return 0;
}

BZOJ.5120.[清华集训2017]无限之环(费用流zkw 黑白染色)的更多相关文章

  1. LOJ 2321 清华集训2017 无限之环 拆点+最小费用最大流

    题面:中文题面,这里不占用篇幅 分析: 看到题面,我就想弃疗…… 但是作为任务题单,还是抄了题解…… 大概就是将每个格子拆点,拆成五个点,上下左右的触点和一个负责连源汇点的点(以下简称本点). 这个这 ...

  2. [清华集训2017]无限之环(infinityloop)

    description 题面 solution 一开始的思路是插头\(DP\),然而复杂度太高 考虑将网格图黑白染色后跑费用流 流量为接口数,费用为操作次数 把一个方格拆成五个点,如何连边请自行脑补 ...

  3. BZOJ 5120: [2017国家集训队测试]无限之环(费用流)

    传送门 解题思路 神仙题.调了一个晚上+半个上午..这道咋看咋都不像图论的题竟然用费用流做,将行+列为奇数的点和偶数的点分开,也就是匹配问题,然后把一个点复制四份,分别代表这个点的上下左右接头,如果有 ...

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

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

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

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

  6. [LOJ#2330]「清华集训 2017」榕树之心

    [LOJ#2330]「清华集训 2017」榕树之心 试题描述 深秋.冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子.相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下.小溪依旧 ...

  7. [LOJ#2329]「清华集训 2017」我的生命已如风中残烛

    [LOJ#2329]「清华集训 2017」我的生命已如风中残烛 试题描述 九条可怜是一个贪玩的女孩子. 这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\ ...

  8. Loj #2331. 「清华集训 2017」某位歌姬的故事

    Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...

  9. Loj #2324. 「清华集训 2017」小 Y 和二叉树

    Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...

随机推荐

  1. HTTP协议中PUT和POST使用上的区别

    有的观点认为,应该用POST来创建一个资源,用PUT来更新一个资源:有的观点认为,应该用PUT来创建一个资源,用POST来更新一个资源:还有的观点认为可以用PUT和POST中任何一个来做创建或者更新一 ...

  2. 那些IT行业的经典定律

    几十年来,IT界有一些非常著名的定律,蕴含着行业发展的大智慧,非常有趣,略作收集总结,再加上一丁点自己的浅见~ 一.摩尔定律:价格不变,集成电路上可容纳的元器件数目,约每隔18个月便会翻一倍,性能也将 ...

  3. 【vim】实时计算器

    在插入模式下,你可以使用 Ctrl+r 键然后输入 =,再输入一个简单的算式.按 Enter 键,计算结果就会插入到文件中.例如,尝试输入: Ctrl+r '=2+2' ENTER 然后计算结果&qu ...

  4. Linux Kernel 代码艺术——编译时断言【转】

    转自:http://www.cnblogs.com/hazir/p/static_assert_macro.html 本系列文章主要写我在阅读Linux内核过程中,关注的比较难以理解但又设计巧妙的代码 ...

  5. memset()函数

    memset需要的头文件 <memory.h> or <string.h> memset <wchar.h> wmemset  函数介绍 void *memset( ...

  6. 【转】#ifdef __cplusplus+extern "C"的用法

    时常看到别人的头文件中,有这样的代码: #ifdef __cplusplus extern "C" { #endif //一段代码 #ifdef __cplusplus } #en ...

  7. centos环境自动化批量安装jdk软件脚本

    自动化安装jdk软件部署脚本 准备工作: 1.在执行脚本的服务器上生成免密码公钥: 安装expect命令 yum install -y expect ssh-keygen 三次回车 2.将jdk-7u ...

  8. python flask安装

    windows环境上,打开命令行,输入pip  list  检查列表中是否安装过flask 安装flask命令:pip install flask 出现Successfully installed等提 ...

  9. Python-元类 单例

    2.元类 用于创建类的类 叫元类 默认元类都是type 主要控制类的创建__init__ 类的实例化__call__ 3.单例 一个类只有一个实例 什么时候用,大家公用一个打印机,没有必要每个人创建新 ...

  10. IntelliJ IDEA 下的svn配置及使用的非常详细的图文总结

    首先,使用的时候,自己得先在电脑上安装个小乌龟.也就是svn啦. 第一步安装小乌龟. 如下: 具体安装好像没什么具体要求,一路next,就好. 如上图箭头所示,在安装 TortoiseSVN 的时候, ...