hlpp(欢乐婆婆)算法总结

突然发现咕了好久(X)

  emm先大概说一下,hlpp是针对网络流算法的一种复杂度更优的算法,基于预流推进(即模拟) 复杂度上界为 n2根号m 且跑不满

(所以学会了它,可以解决绝大部分dinic能解决的问题,以及绝大部分dinic不能解决的问题

先把以前的dinic算法放一下吧

你谷P3376 网络最大流模板

#include<bits/stdc++.h>
#define re register
using namespace std;
const int maxxx=(1ll<<)-;
inline int read()
{
register int x=,f=; char ch=getchar();
while(ch<''||ch>'') {if(ch=='-') f=-; ch=getchar();}
while(ch>=''&&ch<='') {x=x*+ch-''; ch=getchar();}
return x*f;
}
struct node
{
int to,nxt,dis;
}e[];
int head[],cur[],cnt=-;
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].dis=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int d[],n,m,s,t;
bool bfs()
{
queue<int> q;
q.push(s);
memset(d,-,sizeof(d));
d[s]=;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-;i=e[i].nxt)
{
int v=e[i].to;
if(e[i].dis && d[v]==-)
{
d[v]=d[u]+;
q.push(v);
}
}
}
return (d[t]!=-);
}
int dfs(int u,int flow)
{
if(u==t) return flow;
int tmp=,newflow;
for(int i=cur[u ];i!=-;i=e[i].nxt)
{
int v=e[i].to;
if(d[v]==d[u]+ && e[i].dis)
{
newflow=dfs(v,min(flow,e[i].dis));
flow-=newflow;
e[i].dis-=newflow;
tmp+=newflow;
e[i^].dis+=newflow;
if(!flow) break;
}
}
if(!tmp) d[u]=-;
return tmp;
}
void dinic()
{
int maxflow=;
while(bfs())
{
for(re int i=;i<=n;++i) cur[i]=head[i];
maxflow+=dfs(s,maxxx);
}
printf("%d\n",maxflow);
}
int main()
{
memset(head,-,sizeof(head));
n=read(); m=read(); s=read(); t=read();
for(re int i=;i<=m;++i)
{
int x,y,z;
x=read(); y=read(); z=read();
add(x,y,z); add(y,x,);
}
dinic();
}

  Dinic算法的基本思想就是找增广路,去进行流量增广的一种(贪心?)算法。然后为了能够保证正确性,反向给自己反悔的机会

(咱也不知道模拟费用流啥的算法是啥

  但是这个HLPP算法呢,并不是基于增广路算法的网络流,而是基于另一种(贪心?)算法,去进行预流推进

(循环屁放了第二遍)

  基本思想:

  先说一下基本思想吧: 这个算法是允许每个点储存一个流量的(超额流),不过要保证到了最后除了源点汇点以外的点超额流为0(达到动态平衡(笑))

  所以为了每个节点储存的超额流能够推送出去,引入了高度的概念(越来越像模拟了)

  同时在引入了高度后,就可以避免两个节点互相推送超额流的情况

  不过,想一想,为什么,在现实生活中的水坑,就是一个例子,我们把超额流推给低的节点,结果有的水流就积攒到了这个水坑里,因为四周都比他高,而可怜的水坑

  承受着巨大的超额流结果推送不出去,我们就死循环了。

  所以咋办呢?  要抬高这样的点的高度(废话

  这个操作叫做重贴标签

  (什么最小顶标和啊什么的我是完全不会

  具体实现过程:

  我们使用e[i]表示一个点的超额流,h[i]表示一个点的高度

  从汇点开始进行bfs赋值高度(这里与网络流的分层图不同,为了保证可以流到汇点,必须让高度递增

  每次处理高度最高且超额流不为0的点(用优先队列维护) ,并对其进行推流操作把所有能推的都尽量推出去,不用担心正确性,因为...

  就算不对也可以让人家再退回来鸭!

  所以算法的正确性一目了然,和增广路的贪心反悔是相同的

    接下来如果e[u]还是不等于0,就要进行重贴标签,去抬高高度继续等待颓推流

  最后如果除了源点汇点,其他超额流都是0,那么说明方案合法,此时汇点的超额流就是原图最大流

  优化:

  为了这个优秀的算法在实现时能完全优于增广路算法,加入一个船新优化(该优化比增广路中当前弧优化更优

  GAP优化

  我们还可以发现如果一个点v在被重贴标签以后,如果它原来的高度已经没有其它点,那么高于它的点一定不能将流量推送到t了。

  所以我们可以将高度大于h[v]​且小于n+1的点高度设置为n+1,以便尽快将流量推送给s。

  对于如何判断这个高度已经没有其它节点,可以和ISAP一样用一个gap数组来计数,这就是HLPP的gap优化。

  具体实现:

  

#include<bits/stdc++.h>
#define re register
#define il inline
#define inc(i,j,k) for(re int i=j;i<=k;++i)
#define ra(i,u) for(re int i=head[u];i!=-1;i=a[i].nxt)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxm=;
const int maxn=;
struct node
{
int to,nxt,flow;
}a[maxm<<];
int head[maxn],gap[maxn],h[maxn],e[maxn];
bool vis[maxn];
int cnt=-,n,m,st,ed;
struct cmp {il bool operator () (int x,int y)const{return h[x]<h[y];}};
priority_queue<int,vector<int>,cmp> pq;
queue<int> q;
il void add(int u,int v,int w)
{
a[++cnt].to=v;
a[cnt].nxt=head[u];
a[cnt].flow=w;
head[u]=cnt;
}
il bool bfs()
{
memset(h,inf,sizeof(h));
h[ed]=;
q.push(ed);
while(!q.empty())
{
int t=q.front();
q.pop();
ra(i,t)
{
int v=a[i].to;
if(a[i^].flow && h[v]>h[t]+)
{
h[v]=h[t]+;
q.push(v);
}
}
}
return h[st]!=inf;
}
il void push(int u)
{
ra(i,u)
{
int v=a[i].to;
if((a[i].flow) && (h[v]+==h[u]))
{
int df=min(e[u],a[i].flow);
a[i].flow-=df;
a[i^].flow+=df;
e[u]-=df;
e[v]+=df;
if((v!=st)&&(v!=ed)&&(!vis[v]))
{
pq.push(v);
vis[v]=;
}
if(!e[u])break;
}
}
}
il void relabel(int u)
{
h[u]=inf;
ra(i,u)
{
int v=a[i].to;
if((a[i].flow)&&(h[v]+<h[u]))h[u]=h[v]+;
}
}
inline int hlpp()
{
if(!bfs())return ;
h[st]=n;
memset(gap,,sizeof(gap));
inc(i,,n) if(h[i]!=inf)gap[h[i]]++;
ra(i,st)
{
int v=a[i].to;
if(int f=a[i].flow)
{
a[i].flow-=f;a[i^].flow+=f;
e[st]-=f;e[v]+=f;
if(v!=st&&v!=ed&&!vis[v])
{
pq.push(v);
vis[v]=;
}
}
}
while(!pq.empty())
{
int t=pq.top();pq.pop();
vis[t]=;push(t);
if(e[t])
{
gap[h[t]]--;
if(!gap[h[t]])
{
inc(v,,n)
{
if(v!=st&&v!=ed&&h[v]>h[t]&&h[v]<n+)
{
h[v]=n+;
}
}
}
relabel(t);gap[h[t]]++;
pq.push(t);vis[t]=;
}
}
return e[ed];
}
signed main()
{
memset(head,-,sizeof(head));
scanf("%d%d%d%d",&n,&m,&st,&ed);
inc(i,,m)
{
int x,y;
ll f;
scanf("%d%d%lld",&x,&y,&f);
add(x,y,f);
add(y,x,);
}
ll maxf=hlpp();
printf("%lld",maxf);
return ;
}

这是我照着题解一点一点写的,例题的话等以后再更(窝就是太弱了

最大流——预流推进

咕咕咕-HLPP算法的更多相关文章

  1. u-boot for tiny210 ver1.0(by liukun321咕唧咕唧)

     新版本下载: 下面的链接提供了较新版本的源码 ver4.0源码下载:u-boot for tiny210 ver4.0 ver3.1源码下载: u-boot for tiny210 ver3.1 v ...

  2. (转)S5pv210 HDMI 接口在 Linux 3.0.8 驱动框架解析 (By liukun321 咕唧咕唧)

    作者:liukun321 咕唧咕唧 日期:2014.1.18 转载请标明作者.出处:http://blog.csdn.net/liukun321/article/details/18452663 本文 ...

  3. FT5X06 如何应用在10寸电容屏(linux-3.5电容屏驱动简析&移植10寸电容屏驱动到Android4.2) (by liukun321咕唧咕唧)

    这是几个月以前的东西了,在彻底遗忘之前拿出来好好写写.做个笔记,也算是造福后来人了.在做这个项目之前,没有做过电容屏的驱动,印象中的电容触摸屏是不需要校正的.IC支持多大的屏就要配多大的屏.但是拿到需 ...

  4. linux多线程驱动中调用udelay()对整个系统造成的影响(by liukun321咕唧咕唧)

    以前没考虑过这个问题,而且之前可能运气比较好,虽然用了udelay但也没出什么奇怪的问题,今天在 CSDN上看到了一篇关于此问题帖子,觉得很受用,再此做简要的记录和分析: 驱动开的是内核线程 跟普通进 ...

  5. 基于S5pv210流媒体server的实现之网络摄像头(by liukun321 咕唧咕唧)

    这里仅介绍流媒体server端的实现思路.及编码注意问题,不会贴代码的详细实现. 直接入正题先介绍一下系统硬件框架: server端连接PC机用VLC播放例如以下图: server端应用程序能够分为图 ...

  6. [学习笔记] 网络最大流的HLPP算法

    #define \(u\)的伴点集合 与\(u\)相隔一条边的且\(u\)能达到的点的集合 \(0x00~ {}~Preface\) \(HLPP(Highest~Label~Preflow~Push ...

  7. HLPP算法 一种高效的网络最大流算法

    #include <algorithm> #include <cstdio> #include <cctype> #include <queue> #d ...

  8. 算法课上机实验(一个简单的GUI排序算法比较程序)

    (在家里的电脑上Linux Deepin截的图,屏幕大一点的话,deepin用着还挺不错的说) 这个应该是大二的算法课程上机实验时做的一个小程序,也是我的第一个GUI小程序,实现什么的都记不清了,只记 ...

  9. 索引(Awakening!)

    orz写个索引,方便日后复习和补充. 目前笔记还不是很多,而且写得比较烂,望各位到访的巨佬谅解. 大概可以算作一个归纳总结? ……没链接的还没开始写或者没写完,而且不知道什么时候才能写完(咕咕咕) 一 ...

随机推荐

  1. CSS边框使用-基础

    前端开发工作中经常会碰到奇形怪状的图形,当然也少不了UI设计童鞋的脑洞和创意啦,初级的开发人员可能会选择使用图片做背景加上位置属性实现,不过很多时候,CSS能实现的就不要再动用PS等工具了,时间宝贵, ...

  2. 如何通过纯javascript实现表单提交

    通常,如果是POST方法,一般使用vuejs+axios,或使用Jquery实现表单提交.有些地方,我想使用纯JS实现,比方简单的登陆跳转.话不多说,看原代码, laravel中的HTML部分,如果不 ...

  3. Macbook触控板使用技巧

    1. 在Storyboard鼠标右键可以直接拖线的,如果你用的是外接的第三方鼠标,没必要按着 control 键再用鼠标左键拖线 如果是触控板的话,双指按下去就可以直接拖线,带3Dtouch功能的触控 ...

  4. 真机调试(A valid provisioning profile for this executable was not found.)

    这个问题是因为provisioning的问题,因为真机没有加入到账号下面的原因 解决步骤 1.吧identifier复制然后再平开开发中心 2.点击+号添加设备保存 3.在develope中选中保存即 ...

  5. 计算机基础 python入门

    1.计算机基础 计算机组成: 输入输出设备内. 存储器 .cpu .电源 .显卡 中央处理器(cpu) 处理各种数据 相当于人的大脑 内存 存储数据 相当于临时记忆 硬盘 存储数据 相当于人的永久记忆 ...

  6. 如何把转入成功的XXX.sql导入到自己的数据库里

    1.新建自己的mysql连接,mysql连接名随便起,如cxf  密码尽量写123456或者root,防止忘记.按照图示右键(如果想在已有的mysql连接基础上建立数据库连接直接看第二步) 2.右键名 ...

  7. c# WF 第1节 创建winform程序

    本节内容: 1:vs的RAD 2:WinForm的创建简介 3:创建窗口与控制台程序文件的对比 4:窗口文件内容 5:winform怎么运行 6:winform的实质 1:vs的RAD 2:WinFo ...

  8. 题解:T103180 しろは的军训列队

    题目链接 solution: 按题目随便假设找到了一个x,它的位置的ap,属性bp 看下图 $$$$$$$$$$$$$$$$|||||P &&&&&&& ...

  9. 一、man、系统工作、系统检测命令

    目录 一.man命令 (一)常用按键 (二)结构意义 二.常用系统工作命令 (一) echo (二)date (三)reboot (四)powoff (五)wget (六)ps (七)top (八)p ...

  10. Eclipse中如何安装Git插件

    现在的Eclipse一般都自带Git插件. 检查Eclipse是否有Git插件: 方法一:Help—>About Eclipse,出现下面的图标,说明Eclipse中已有Git插件,就不用安装了 ...