图论-网络流⑦-费用流解题

上一篇:图论-网络流⑥-费用流

下一篇:图论-网络流⑧-有上下界的网络流

参考文献:

https://www.luogu.com.cn/blog/user9012/solution-p3980

大纲

  • 什么是网络流
  • 最大流(最小割)
  • D i n i c Dinic Dinic (常用)
  • E K EK EK
  • S a p Sap Sap
  • F o r d − F u l k e r s o n Ford-Fulkerson Ford−Fulkerson(不讲)
  • H L P P HLPP HLPP (快)
  • 最大流解题
  • 费用流
  • E K EK EK 费用流
  • D i n i c Dinic Dinic 费用流
  • z k w zkw zkw 费用流
  • 费用流解题 Start \color{#33cc00}\texttt{Start} Start End \color{red}\texttt{End} End

  • 有上下界的网络流

  • 无源汇上下界可行流
  • 有源汇上下界可行流
  • 有源汇上下界最大流
  • 有源汇上下界最小流
  • 最大权闭合子图
  • 有上下界的网络流解题

前文中讲了费用流的定义和 3 3 3 种费用流算法,这一篇中会讲几道费用流的经典例题。

费用流解题

费用流的解题套路和最大流差不多,无非就是多加了个费用条件。因为费用流的算法时间复杂度很玄学,所以有些时候优化很重要。

[HAOI2010]订货

普通的费用流题目,只需要按图索骥即可。


同最大流解题思路,源点表示发货商,汇点表示该公司的客户。每个月都可以买无穷产品,所以 s s s 向每个月节点连流量为 ∞ \infty ∞ 费用为 d i d_i di​ 的边;每个月节点向 t t t 连流量为 U i U_i Ui​ 的边(满足最大流就相当于满足客户),因为每个月还可以储存,所以每个月节点向下个月节点(如果有下个月节点)连流量为 S S S 费用为 m m m 的边。 跑个最小费用最大流,总费用就是最低成本。

整理一下:

D i n i c Dinic Dinic费用流代码(以前写的,码风很蒻):

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
const int M=6e6+10;
const int inf=1e8+10;
int n,m,S,s,t,fans,cosans;
struct edge{int adj,nex,fw,r;
}e[M];
int g[N],top=1;
void add(int x,int y,int z,int w){e[++top]=(edge){y,g[x],z,w};g[x]=top;
}
int dep[N],cur[N];
bool vis[N];
queue<int> Q;
bool spfa(){// puts("spfa()");for(int i=1;i<=n;i++)vis[i]=0,dep[i]=inf;Q.push(s),vis[s]=1,dep[s]=0;for(int i=1;i<=n;cur[i]=g[i],i++);while(Q.size()){int x=Q.front(); Q.pop();vis[x]=0;for(int i=g[x];i;i=e[i].nex){int to=e[i].adj,d=e[i].r;if(e[i].fw&&dep[to]>dep[x]+d){dep[to]=dep[x]+d;if(!vis[to]){vis[to]=1;Q.push(to);}}}}return dep[t]!=inf;
}
int dfs(int x,int F){// puts("dfs");if(!F||x==t)return F;int flow=0,f;vis[x]=1;for(int i=cur[x];i;i=e[i].nex){int to=e[i].adj; cur[x]=i;if(!vis[to]&&dep[x]+e[i].r==dep[to]&&(f=dfs(to,min(F,e[i].fw)))>0){e[i].fw-=f;e[i^1].fw+=f;flow+=f,F-=f;if(!F){vis[x]=0;break;} }}return flow;
}
int main(){scanf("%d%d%d",&n,&m,&S);for(int i=1,x;i<=n;i++){scanf("%d",&x);add(i+1,n+2,x,0);add(n+2,i+1,0,0);}for(int i=1,x;i<=n;i++){scanf("%d",&x);add(1,i+1,inf,x);add(i+1,1,0,-x);}for(int i=1;i<n;i++)add(i+1,i+2,S,m),add(i+2,i+1,0,-m);s=1; n=t=n+2;while(spfa()){int d=dfs(s,inf);fans+=d,cosans+=d*dep[t];}printf("%d\n",cosans);return 0;
}

总结:普通费用流题按图索骥即可。

[SDOI2010]星际竞速

思维难度排名前 10 % 10\% 10%,蒟蒻以前写的题解:传送门


我把这题的思想叫做接力棒思想,因为我就是想到接力棒的时候突然知道怎么做的。这题你想一个流遍历所有点是不可能的,因为这道题正好否决了所以这样的方法。

正解就像在接力跑。想象有 n + 1 n+1 n+1 个人接力跑 ,刚开始时都在 s s s 点上,分别对应 s s s 和 1 ∼ n 1\sim n 1∼n 这 n + 1 n+1 n+1 个节点,开始时接力棒在 s s s 那个人手上。
1 ∼ n 1\sim n 1∼n 算操场里的点,开始时 s s s 对应的运动员开跑。在未经召唤的情况下从场外到场内节点 i i i 需要花费 a i a_i ai​,所以 s s s 运动员就花费某个 a x a_x ax​ 到场内节点 x x x,然后到达节点后打卡,休息。
然后 x x x 节点对应的运动员受到 s s s 的召唤,免费瞬移到 x x x 节点,然后沿着道路花费相应的费用到另一个节点,并打卡,休息,召唤该节点对应的运动员。
然后反复这个过程,除了 s s s 节点对应的运动员,别的运动员也可以花费 a i a_i ai​ 的费用跑到场内,或免费受召唤瞬移,最终所有 1 ∼ n 1\sim n 1∼n 节点被打卡一次后,接力赛结束。
然后按照被转化的问题,按图索骥一下建个图,最后的最大流最小费用就是答案。

整理一下:

注1:这个图表示样例 1 1 1 的连边方法。
注2:题目中说只能星际航行到引力大的星球。
注3:图中的边流量都为 1 1 1。

AC \color{#7d0}\texttt{AC} AC 代码:

#include <bits/stdc++.h>
using namespace std;
const int N=2e3+10;
const int M=2e6+10;
const int inf=1e8;
int d(){int x; scanf("%d",&x); return x;}
int n,m,p,s,t,a[N],fans,cans;
struct edge{int adj,nex,fw,r;
}e[M];
int g[N],top=1;
void add(int x,int y,int z,int w){e[++top]=(edge){y,g[x],z,w};g[x]=top;
}
void Add(int x,int y,int z,int w){// printf("%d-%d %d %d\n",x,y,z,w);add(x,y,z,w),add(y,x,0,-w);
}
int dep[N],cur[N];
bool vis[N];
queue<int> Q;
bool spfa(){for(int i=1;i<=p;i++)vis[i]=0,dep[i]=inf,cur[i]=g[i];Q.push(s),vis[s]=1,dep[s]=0;while(Q.size()){int x=Q.front(); Q.pop();vis[x]=0;for(int i=g[x];i;i=e[i].nex){int to=e[i].adj,d=e[i].r;if(e[i].fw&&dep[to]>dep[x]+d){dep[to]=dep[x]+d;if(!vis[to]){vis[to]=1;Q.push(to);}}}}return dep[t]!=inf;
}
int dfs(int x,int F){if(!F||x==t)return F;int flow=0,f;vis[x]=1;for(int i=cur[x];i;i=e[i].nex){int to=e[i].adj; cur[x]=i;if(!vis[to]&&dep[x]+e[i].r==dep[to]&&(f=dfs(to,min(F,e[i].fw)))>0){e[i].fw-=f;e[i^1].fw+=f;flow+=f,F-=f;if(!F){vis[x]=0;break;} }}return flow;
}
int main(){n=d(),m=d(),p=t=2*n+2,s=t-1;for(int i=1,x;i<=n;i++){a[i]=d(); Add(i+n,t,1,0);Add(s,i,1,0),Add(s,i+n,1,a[i]);}for(int i=1;i<=m;i++){int x=d(),y=d(),z=d();if(x>y) swap(x,y);if(z<a[y]) Add(x,y+n,1,z);}while(spfa()){int D=dfs(s,inf);fans+=D,cans+=D*dep[t];}printf("%d\n",cans);return 0;
}

总结:网络流题一定、一定、一定要多思考。

[NOI2008]志愿者招募

本蒟蒻做过的最巧妙的费用流题目(没有之一),很谔谔。


源点连志愿者,志愿者连控制的天(区间),天连汇点是最典型的爆〇方式。这题的思想有点像有些比较抠的差分约束题的思想但又不是。

正解就像在闯关。每一天就是一关,开始时你有 ∞ \infty ∞ 个小人,到第 n + 1 n+1 n+1 关时还有这么多小人,就赢了。 第 i i i 天只能免费通过 ∞ − a i \infty-a_i ∞−ai​ (注:这里的 ∞ \infty ∞ 是有数值的, ∞ ≠ ∞ − a i \infty\neq\infty-a_i ∞​=∞−ai​)个人,剩下的人需要乘坐不免费的时空穿越机。
每个志愿者就是一台能从 s i s_i si​ 关跳到 t i + 1 t_i+1 ti​+1 关的时空穿越机,花费为 c i c_i ci​,只能乘坐一个小人。 但因为每种志愿者有无限个,所以可以看作能乘坐无穷小人,费用为 c i / c_i/ ci​/人。
求最后赢得所有 n + 1 n+1 n+1 关最少的费用。然后按图索骥建个图,跑个最大流最小费用就是答案。

整理一下:

注:图为题目样例。

AC \color{#499}\texttt{AC} AC 代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1e3+10;
const int M=1e4+10;
const int P=2e4+10;
const int E=3e7+10;
const int inf=1e8;
int d(){int x;scanf("%d",&x);return x;}
int n,m,p,s,t,fans,cans;
struct edge{int adj,nex,fw,r;
}e[E];
int g[P],top=1;
void add(int x,int y,int w,int r){e[++top]=(edge){y,g[x],w,r};g[x]=top;
}
void Add(int x,int y,int w,int r){add(x,y,w,r),add(y,x,0,-r);
}
int dep[P],cur[P];
bool vis[P];
queue<int> q;
bool spfa(){for(int i=1;i<=p;i++)vis[i]=0,dep[i]=inf,cur[i]=g[i];q.push(s),vis[s]=1,dep[s]=0;while(q.size()){int x=q.front();q.pop(),vis[x]=0;for(int i=g[x];i;i=e[i].nex){int to=e[i].adj,d=e[i].r;if(e[i].fw&&dep[to]>dep[x]+d){dep[to]=dep[x]+d;if(!vis[to]) vis[to]=1,q.push(to);}}}return dep[t]!=inf;
}
int dfs(int x,int F){if(!F||x==t)return F;int flow=0,f;vis[x]=1;for(int i=cur[x];i;i=e[i].nex){int to=e[i].adj; cur[x]=i;if(!vis[to]&&dep[x]+e[i].r==dep[to]&&(f=dfs(to,min(F,e[i].fw)))>0){e[i].fw-=f,e[i^1].fw+=f;flow+=f,F-=f;if(!F){vis[x]=0; break;}}}return flow;
}
int main(){n=d(),m=d();p=t=n+m+3,s=t-1;for(int i=1;i<=n;i++)Add(i,i+1,inf-d(),0);for(int i=1;i<=m;i++){int S=d(),T=d(),C=d();Add(S,T+1,inf,C);}Add(s,1,inf,0),Add(n+1,t,inf,0);while(spfa()){int D=dfs(s,inf);fans+=D;cans+=dep[t]*D;}printf("%d\n",cans);
}

总结:有什么好总结的呢,脑洞和思考最重要吧。

下一篇会讲有关有上下界的网络流的知识。

祝大家学习愉快!

图论-网络流⑦-费用流解题相关推荐

  1. 图论 —— 网络流 —— 费用流 —— 基于 Dijkstra 的费用流

    [概述] 在求解费用流时,大多数情况都是使用基于 SPFA 的 MCMF 算法,但有时某些毒瘤题会卡 SPFA,此时就要利用基于 Dijkstra 的费用流来求解. [算法原理] 基于 Dijkstr ...

  2. 图论 —— 网络流 —— 费用流 —— zkw 费用流

    [概述] 求解费用流的方法很多,目前最流行的是 MCMF 费用流,其实质是将 EK 算法中的 bfs 换为了 SPFA 来计算最小费用,但其存在的一个缺点是 EK 是单路增广的,这样速度会相应的慢一些 ...

  3. 图论 —— 网络流 —— 费用流 —— MCMF 算法

    [概述] EK 算法是每次用广搜寻找一条最短的增广路,然后沿其增广,而 MCMF 算法是在 EK 算法的基础上,每次用 SPFA 计算图的距离标号,然后沿着可行边进行增广,即将 EK 算法中的 bfs ...

  4. jzoj3302-[集训队互测2013]供电网络【上下界网络流,费用流,动态加边】

    正题 题目大意 若干个城市一些城市有一定的电,有些城市需要一定的电.对于第iii个城市购买一个电需要iniin_iini​,送出电需要outiout_iouti​.当然城市之间也可以相互传输电. 对于 ...

  5. P2517-订货【网络流,费用流】

    正题 AC链接: https://www.luogu.org/record/show?rid=7949532 大意 有n个月,每个月商品价格di,需求量Ui.有容量为S的仓库,一个商品汇存一个月要m. ...

  6. 【HDU - 6118】度度熊的交易计划(最小费用可行流,网络流费用流变形 )

    题干: 度度熊参与了喵哈哈村的商业大会,但是这次商业大会遇到了一个难题: 喵哈哈村以及周围的村庄可以看做是一共由n个片区,m条公路组成的地区. 由于生产能力的区别,第i个片区能够花费a[i]元生产1个 ...

  7. 修车——网络流——费用流

    这题很明显的是费用流了,关键是在于建图.其实,网络流的关键也是难点就在于建图. n个师傅,m个车-- 根据题意,需要把每个师傅拆成m个点,每辆车再向这些点连接,再来个超级源点S,超级汇点T即可. 代码 ...

  8. P4249 [WC2007]剪刀石头布(网络流/费用流)

    P4249 [WC2007]剪刀石头布 在一个竞赛图上一些边的方向已经确定,但是还有一些边的方向没有确定,求解最多有多少三元环. 首先看到三元环个数,按照套路我们利用度数计算,然后考虑每一条边,每一条 ...

  9. 网络流 费用流 模板 ISAP+SPFA+ZKW

    2020年4月20日重新发布.7年前的文章,几年前CSDN改版的时候变成了私密--重新发一下吧. 关于费用流ZKW算法的讲解:从入门到精通: 最小费用流的"zkw算法" 关于Din ...

最新文章

  1. Radboud科学家探索材料「量子大脑」——没有软件,硬件也可以实现机器学习 | AI日报...
  2. java.lang.IllegalArgumentException: pointerIndex out of range
  3. Hibernate Annotation _List/Map
  4. java 写jsp_Java开发之JSP指令
  5. php获取域名方法,PHP实现获取域名的方法小结
  6. 如何教女朋友学 Python?
  7. xmlDocument来操作xml文档
  8. 简单的移动端图片裁剪vue插件[旋转,平移,缩放,印花]
  9. 水仙花数c语言程序vb,VB编程:求水仙花数
  10. Java开发手册、帮助文档
  11. mysql 5.7版本下载安装
  12. JAVA实现UDP单播组播
  13. this.$refs使用方法
  14. 最短路径--狄克斯特拉(Dijkstra)算法
  15. 一种无监督人脸聚类方法(SOTA效果)
  16. 辽宁初级消防设施操作员考试真题及答案解析
  17. 智慧养老之智慧养老监护平台,为智慧养老保驾护航-新导智能
  18. 地表最强gVim编写Verilog插件 automatic-verilog
  19. JLINK在ADS中的调试心得
  20. 获取墨墨背单词里面的单词书中的单词

热门文章

  1. alpine安装图形界面Xfce
  2. VSCODE 设置自动补全括号
  3. uni-app 66聊天类chat.js封装(三)
  4. MySQL基础篇——第11章 DML(数据操作):增删改
  5. 一:测试小白进阶论(中)
  6. 韩国济州魔米(JEJUMAMI)推出产自洁净济州的儿童零食“小小谷片”
  7. 基于nodejs爬虫
  8. flutter 参数函数_Flutter 基本用法摘记
  9. 用Python生成抖音字符视频!
  10. 方舱医院隔离酒店选择ip可视对讲机的理由