题意简述

有一个\(n\times m\)棋盘,棋盘上每个格子上有一个水管。水管共有\(16\)种,用一个\(4\)位二进制数来表示当前水管向上、右、下、左有个接口。你可以旋转除了\((0101)_2\)和\((1010)_2\)的其他水管,求最少的旋转次数使得水管的每一个接口所在方向都有一个相邻的水管接口与其对应。

做法

这种插头相关的题一般都是网络流,虽然据说插头dp可以过此题。这道题里面要求不存在单独的接口,所有的接口一一对应等价于流量平衡,那么最少的旋转次数就应该想到费用流。

首先把整个棋盘染色,黑点连源点,白格连汇点,这些源汇点的节点流量限制为自己的格子内的接头数。那么如果流量网络满流,那就是此题有解,可以构成流量平衡的情况。

对于每个格子,上下左右拆成四个点,表示这个格子的\(4\)个方向,他们的节点容量为\(1\),用来象征对应接口的流量平衡。

对于一开始不能到达的方向,从初始方向连边至目标方向,费用为最少旋转次数,这样无论怎么旋转,接口对应关系依然存在。

代码实现

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define in inline
#define ak *
#define inf 1e9
#define loc(i,j) ((i-1)*m+j)
int col,cnt=1,n,m,s,t,h[10005],dis[10005],l,r,q[10005],pre[10005],vis[10005],mp[10005],ans,maxflow,preflow;
struct did{
int u,next,to,f,w;
}e[1200005];
char qwq;
in int read()
{
int yz=0,ioi=1;qwq=getchar();
while(qwq<'0'||qwq>'9') ioi=qwq=='-'?~ioi+1:1,qwq=getchar();
while(qwq>='0'&&qwq<='9') yz=(yz<<3)+(yz<<1)+(qwq^48),qwq=getchar();
return yz ak ioi;
}
in void add(re a,re b,re c,re d)
{
if(!col) swap(a,b);
e[++cnt]=(did){a,h[a],b,c,d},h[a]=cnt;
e[++cnt]=(did){b,h[b],a,0,-d},h[b]=cnt;
}
int spfa()
{
memset(pre,0,sizeof(pre));memset(vis,0,sizeof(vis));
for(re i=s;i<=t;i++) dis[i]=i==s?0:inf;
queue<int>q;q.push(s);vis[s]=1;
while(!q.empty())
{
re i=q.front();vis[i]=0;q.pop();
for(re j=h[i],k;k=e[j].to,j;j=e[j].next)
if(e[j].f&&dis[k]>dis[i]+e[j].w)
{
dis[k]=dis[i]+e[j].w;pre[k]=j;
if(!vis[k]) q.push(k),vis[k]=1;
}
}
return dis[t]<inf;
}
in void solve()
{
re delta=inf;
for(re i=t,p;p=pre[i],i!=s;i=e[p^1].to)
delta=min(delta,e[p].f);
for(re i=t,p;p=pre[i],i!=s;i=e[p^1].to)
e[p].f-=delta,e[p^1].f+=delta;
maxflow+=delta;ans+=delta*dis[t];
}
in int f(re i,re j) {return i*(n*m+1)+j;}
in int g(re i,re j) {return (i-1)*m+j;}
in bool ok(re i,re j) {return (i>=1&&i<=n&&j>=1&&j<=m);}
in void turn(re p,re x,re y,re z) {add(f(x,p),f(y,p),1,z);}
int main()
{
// freopen("infinityloop.in","r",stdin);
// freopen("infinityloop.out","w",stdout);
n=read(),m=read(); s=0;t=4*(n*m+1)+4;
for(re i=1;i<=n;i++) for(re j=1;j<=m;j++) mp[g(i,j)]=read();
for(re i=1;i<=n;i++) for(re j=1;j<=m;j++)
{
re p=g(i,j);col=(i+j)%2;
if(col)
{
for(re k=0;k<=3;k++)
if((mp[p]>>k)&1) add(s,f(k,p),1,0),preflow++;
}
else
{
for(re k=0;k<=3;k++)
if((mp[p]>>k)&1) add(t,f(k,p),1,0);
}
if(col)
{
if(ok(i-1,j)) add(f(0,p),f(2,g(i-1,j)),1,0);
if(ok(i,j+1)) add(f(1,p),f(3,g(i,j+1)),1,0);
if(ok(i+1,j)) add(f(2,p),f(0,g(i+1,j)),1,0);
if(ok(i,j-1)) add(f(3,p),f(1,g(i,j-1)),1,0);
}
switch(mp[p])
{
case 0: break; // 0000
case 5: break; // 0101
case 10: break; // 1010
case 15: break; // 1111
case 1: turn(p,0,1,1);turn(p,0,2,2);turn(p,0,3,1);break; // 0001
case 2: turn(p,1,0,1);turn(p,1,3,2);turn(p,1,2,1);break; // 0010
case 4: turn(p,2,1,1);turn(p,2,0,2);turn(p,2,3,1);break; // 0100
case 8: turn(p,3,0,1);turn(p,3,1,2);turn(p,3,2,1);break; // 1000
case 3: turn(p,0,2,1);turn(p,1,3,1);break; // 0011
case 6: turn(p,1,3,1);turn(p,2,0,1);break; // 0110
case 9: turn(p,0,2,1);turn(p,3,1,1);break; // 1001
case 12: turn(p,2,0,1);turn(p,3,1,1);break; // 1100
case 7: turn(p,0,3,1);turn(p,1,3,2);turn(p,2,3,1);break; // 0111
case 11: turn(p,0,2,2);turn(p,1,2,1);turn(p,3,2,1);break; // 1011
case 13: turn(p,0,1,1);turn(p,2,1,1);turn(p,3,1,2);break; // 1101
case 14: turn(p,3,0,1);turn(p,2,0,2);turn(p,1,0,1);break; // 1110
}
}
while(spfa()) solve();
if(maxflow!=preflow) return puts("-1"),0;
cout<<ans<<endl;
}

洛谷P4003 [国家集训队2017]无限之环 网络流 最小费用最大流的更多相关文章

  1. bzoj 5120: [2017国家集训队测试]无限之环【最小费用最大流】

    玄妙的建图-- 这种平衡度数的题按套路是先黑白染色然后分别连ST点,相邻格子连黑向白连费用1流量0的边,然后考虑费用怎么表示 把一个点拆成五个,上下左右中,中间点黑白染色连ST, 对于连S的点,中点连 ...

  2. 【洛谷4542】 [ZJOI2011]营救皮卡丘(最小费用最大流)

    传送门 洛谷 Solution 这是一道神仙题! 考虑这个东西是个啥. emmm,如果两个点要到达,一定不能经过比他们大的. 所以Floyd搞定两点距离然后费用流跑一遍就是答案了! 代码实现 /* m ...

  3. bzoj 5120 [2017国家集训队测试]无限之环——网络流

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5120 旋转的话相当于去掉一个插头.新增一个插头,所以在这两个插头之间连边并带上费用即可. 网 ...

  4. 洛谷$P1935$ [国家集训队]圈地计划 网络流

    正解:最小割 解题报告: 传送门 就文理分科模型嘛$QwQ$?所以就,跑个最小割呗,然后就做完辣?仔细想想细节发现并麻油那么简单嗷$QwQ$ 先考虑如果没有这个$k\cdot C_{i,j}$的贡献就 ...

  5. 洛谷 P4307 [JSOI2009]球队收益 / 球队预算(最小费用最大流)

    题面 luogu 题解 最小费用最大流 先假设剩下\(m\)场比赛,双方全输. 考虑\(i\)赢一局的贡献 \(C_i*(a_i+1)^2+D_i*(b_i-1)^2-C_i*a_i^2-D_i*b_ ...

  6. 模板—点分治A(容斥)(洛谷P2634 [国家集训队]聪聪可可)

    洛谷P2634 [国家集训队]聪聪可可 静态点分治 一开始还以为要把分治树建出来……• 树的结构不发生改变,点权边权都不变,那么我们利用刚刚的思路,有两种具体的分治方法.• A:朴素做法,直接找重心, ...

  7. [洛谷P1527] [国家集训队]矩阵乘法

    洛谷题目链接:[国家集训队]矩阵乘法 题目背景 原 <补丁VS错误>请前往P2761 题目描述 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 输入输出格式 输入 ...

  8. 洛谷P1501 [国家集训队]Tree II(LCT,Splay)

    洛谷题目传送门 关于LCT的其它问题可以参考一下我的LCT总结 一道LCT很好的练习放懒标记技巧的题目. 一开始看到又做加法又做乘法的时候我是有点mengbi的. 然后我想起了模板线段树2...... ...

  9. 洛谷P2619 [国家集训队2]Tree I(带权二分,Kruscal,归并排序)

    洛谷题目传送门 给一个比较有逼格的名词--WQS二分/带权二分/DP凸优化(当然这题不是DP). 用来解决一种特定类型的问题: 有\(n\)个物品,选择每一个都会有相应的权值,需要求出强制选\(nee ...

随机推荐

  1. Appium关键字

    *** Settings *** Library AppiumLibrary Library AutoItLibrary Library os *** Keywords *** xpath应该匹配次数 ...

  2. ARTS-1

    ARTS的初衷 Algorithm:主要是为了编程训练和学习.每周至少做一个 leetcode 的算法题(先从Easy开始,然后再Medium,最后才Hard).进行编程训练,如果不训练你看再多的算法 ...

  3. CSS3——注释 id 和 class 选择器 css创建(外部、内部、内联样式表)

    注释 /*         注释内容          */ id 和 class 选择器 id   ID属性不要以数字开头,数字开头的ID在 Mozilla/Firefox 浏览器中不起作用 < ...

  4. <class 'blog.admin.CategoryAdmin'>: (admin.E108) The value of 'list_display[0]' refers to 'mame', which is not a callable, an attribute of 'CategoryAdmin', or an attribute or method on 'blog.Category'

    系统反馈此类错误是因为行列映射时无法对应: 引起无法对应的原因有: 定义是缺少某列,定义时缩进导致,映射关系时缺少某列,(通俗讲列名对应不上). 这种错误多是python中的 因为其中对于空格的要求是 ...

  5. k8s/01开启云原生之门(Mooc)

    一.kubernetes(k8s)基础知识 1.简介 在2017年Kubernetes战胜了两个强大的竞争对手Swarm和Mesos,成为容器管理与调度编排领域的首选平台和事实标准. 2014年k8s ...

  6. kafka学习(五)

    kafka可靠的数据传递   kafka可靠性保证 ACID 是关系型数据库保证数据的规范,指的是原子性,一致性,隔离性和持久性,这是数据库给出的可靠性保证.   kafka给出的保证是什么? 1.k ...

  7. Powershell 脚本输出前十条消耗内存的进程到excel

    # create new excel instance $objExcel = New-Object -comobject Excel.Application $objExcel.Visible = ...

  8. & 和 && 区别和联系,| 和 || 区别和联系

    & 和 && 区别和联系,| 和 || 区别和联系,实际项目中,什么情况用哪种? 首先,& 和 && 的联系(共同点): & 和 &&a ...

  9. input输入框的的input事件和change事件以及change和blur事件的区别

    input输入框的 oninput事件 ,在用户输入的时候触发,只要元素值发生变化就会触发 input输入框的 onchange事件 ,要在输入框失去焦点的时候触发事件,当鼠标在其他地方点击一下才会触 ...

  10. C/C++ 防止头文件重复包含 #pragma once 与 #ifndef 的区别

    为了避免同一个头文件被多重包含/重复包含,有两种方式: 方式一: #ifndef XXX #define XXX ... ...  //声明语句 #endif  //XXX 方式二: #pragma ...