题目链接

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. ubuntu 安装lightgbm

    前提:安装好git.cmake 1. 安装lightgbm git clone --recursive https://github.com/Microsoft/LightGBM 2. 编译 下载好的 ...

  2. 利用autocomplete.js实现仿百度搜索效果(ajax动态获取后端[C#]数据)

    实现功能描述: 1.实现搜索框的智能提示 2.第二次浏览器缓存结果 3.实现仿百度搜索 <!DOCTYPE html> <html xmlns="http://www.w3 ...

  3. (网络编程)基于tcp(粘包问题) udp协议的套接字通信

    import   socket 1.通信套接字(1人1句)服务端和1个客户端 2.通信循环(1人多句)服务端和1个客户端 3.通信循环(多人(串行)多句)多个客户端(服务端服务死:1个客户端---&g ...

  4. jquery引入

    网络地址:http://code.jquery.com/jquery-2.2.0.min.js 在需要的页面中直接使用网络地址,就不需要本地文件 <script type="text/ ...

  5. 单点登录SSO+鉴权

    一.单点登录原理 1.登录 2.注销 --------------------------------------------------------------------------------- ...

  6. .netcore 整合 log4net

    1.背景 前两天,曾经的一个同事咨询我,怎样将log4net以中间件的形式整合到core里边去.我不假思索的回答,这种问题应该有人做过吧,他说没有.于是,我去博客园搜了下,发现还真没有,全部都是传统. ...

  7. GitHub提交代码后不显示用户名只显示邮箱

    提交完代码如图: 解决方案: 右键git bash here 输入命令如下: git config --global user.name "username" git config ...

  8. Solution of Publishing failed with multiple errors Error copying file static\

    1.前言 由于系统被IT打了防病毒补丁,然后启动web项目一直出现Publishing failed with multiple errors Error copying file static... ...

  9. PHP共享内存详解

    前言 在PHP中有这么一族函数,他们是对UNIX的V IPC函数族的包装. 它们很少被人们用到,但是它们却很强大.巧妙的运用它们,可以让你事倍功半. 它们包括: 信号量(Semaphores) 共享内 ...

  10. MyEclipse 2017 ci6 安装反编译插件(本人自己摸索的方法,亲测可行)

    注: 本文来源于:Smile_Miracle 的< MyEclipse 2017 ci6 安装反编译插件(本人自己摸索的方法,亲测可行) > 第一步:关闭ME,去一下地址下载jad的反编译 ...