网络流与各种建模(I)

网络流基础

这里默认读者学习过网络流和MCMF,这里仅作复习

  • 网络流解决的问题是给一个源点和汇点,每个边有最大流量,最大化从源点放水到汇点的速率。
  • 网络流的时间复杂度是 \(O(nm)\)。对于一些建模可能跑得快一点,也就是跑不满
  • Dinic 的做法是每次先给图 BFS 分层然后跑增广路,每次仅向层数比自身大的点流出。

时间复杂度的证明:不难发现每次 BFS 后的 \(lv[t]\) 必然减少,否则仍然存在一条路径未流完,矛盾。每次增广路是 \(O(m)\) 的,\(lv[t]\leq n\) 所以时间上限是 \(O(nm)\) 的。

  • Dinic 跑二分图最大匹配是 \(O(m\sqrt n)\) 的,快于匈牙利的 \(O(n^3)\)。
  • 我们可以通过跑完 dinic 后的流量变化判断每条边的流量从而构造方案。
  • 使用的 dinic 封装模板(后面的参考代码默认加上该模板)
namespace graph{
const int MAXN=1e5+5;
const int MR=1e6+5;
int las[MAXN],cur[MAXN];
int lv[MAXN],N,s,t;
queue<int> q;
struct edge{
int u,v,w,ne;
}e[MR];int tot=1;
void init(){
tot=1;
for(int i=1;i<=N;i++)
las[i]=0;
}
void add(int u,int v,int w){
++tot;
e[tot].u=u,e[tot].v=v,e[tot].w=w;
e[tot].ne=las[u],las[u]=tot;
return;
}
void Add(int u,int v,int w){
add(u,v,w);
add(v,u,0);
}
bool BFS(){
for(int i=1;i<=N;i++) lv[i]=-1;
lv[s]=0;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=las[u];i;i=e[i].ne){
int to=e[i].v,val=e[i].w;
if(val&&lv[to]==-1){
lv[to]=lv[u]+1;
q.push(to);
}
}
}
return lv[t]!=-1;
}
ll dinic(int u,ll flow){
if(u==t) return flow;
ll rmn=flow;
for(int i=cur[u];i&&rmn;i=e[i].ne){
cur[u]=i;
ll to=e[i].v,val=e[i].w;
if(val>0&&lv[to]==lv[u]+1){
ll c=dinic(to,min(rmn,val));
rmn-=c;
e[i].w-=c;
e[i^1].w+=c;
}
}
return flow-rmn;
}
ll max_flow(){
ll ans=0;
while(BFS()){
for(int i=1;i<=N;i++)
cur[i]=las[i];
ans+=dinic(s,1e18);
}
return ans;
}
}
  • MCMF(费用流)解决的问题是在网络流问题的基础上每个边加上费用,最大化速率的同时要最小费用。
  • 解决方法是把 BFS 分层换成 SPFA 费用分层。
  • 时间复杂度是 \(O(nV)\) 的,\(V\) 是费用的和,证明同上。但是远远跑不满
  • 本篇文章使用的封装费用流模板如下:
namespace graph{
const int MAXN=1e5+5;
const int MR=1e6+5;
struct edge{
int v,w,c,ne;
}e[MR];int cnt=1;
int las[MAXN],cur[MAXN];
int dis[MAXN];bool inq[MAXN],vis[MAXN];
int N,m,s,t;
queue<int> q;
void add(int u,int v,int w,int c){
++cnt;
e[cnt].v=v,e[cnt].w=w,e[cnt].c=c;
e[cnt].ne=las[u],las[u]=cnt;
return;
}
void Add(int u,int v,int w,int c){
add(u,v,w,c);
add(v,u,0,-c);
}
bool SPFA(){
for(int i=1;i<=n;i++)
dis[i]=1e9,inq[i]=0;
dis[s]=0;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();inq[u]=0;
for(int i=las[u];i;i=e[i].ne){
int v=e[i].v,val=e[i].w,cost=e[i].c;
if(val&&dis[v]>dis[u]+cost){
dis[v]=dis[u]+cost;
if(!inq[v]){
inq[v]=true;
q.push(v);
}
}
}
}
return dis[t]!=1e9;
}
int dfs(int u,int flow){
vis[u]=true;
if(u==t) return flow;
int rmn=flow;
for(int i=cur[u];rmn&&i;i=e[i].ne){
cur[u]=i;
int v=e[i].v,val=e[i].w,cost=e[i].c;
if(val&&dis[v]==dis[u]+cost&&!vis[v]){
int fl=dfs(v,min(val,rmn));
rmn-=fl;
e[i].w-=fl;
e[i^1].w+=fl;
}
}
vis[u]=false;
return flow-rmn;
}
ll MCMF(){
ll ans=0,res=0;
while(SPFA()){
for(int i=1;i<=n;i++)
cur[i]=las[i],vis[i]=0;
ll flow=dfs(s,1e9);
ans+=flow,res+=flow*dis[t];
}
return res;
}
}
  • 网络流最困难的地方是建模,也就是我们要把一个问题换成网络流问题。
  • 对于一些没有思路的题,不妨想想网络流。

一些写容易忘记的点

  • 在写 MCMF 时需要给 dinic 函数设立一个 vis 数组,表示当前点是否在 dinic 搜索中,以防有重复环。

网络流建模

二分图问题

二分图最大匹配问题

  • 求二分图最大匹配。
  • 要求 \(O(\sqrt ne)\)。

网络流的常见建模方式是建立超级源点和超级汇点

在本题中我们建立超级源点 \(s\) 和超级汇点 \(t\)。在原图基础上,\(s\) 连所有左部点,所有右部点连 \(t\)。所有边流量为 \(1\)(这里保证了每个点、边都只会被使用一次)。

二分图最大权完美匹配问题

  • 求二分图最大权完美匹配。没有输 -1
  • \(n\leq 300\)。

在上题的基础上增加费用限制。源点汇点的边费用均为 \(0\),中间边费用给定。跑一遍最大费用。

【例题】路径覆盖问题

  • 给 DAG,求最少的路径覆盖所有点。路径不可重点。
  • \(n\leq10^3\)。

DAG 所提供的条件在于我们可以随便连一个没有入边的节点。

我们把问题转换成二分图最大匹配问题。一个节点拆成两个,一个匹配出边,一个匹配入边。这样答案就是点数减去匹配数。

【习题】P2763 试题库问题

  • \(n\) 个物品,物品有多个标签。
  • 构造,把一些物品分到标签类里面,给定每个标签类 \(i\) 的大小 \(r_i\)。
  • \(n\leq 10^3\),不同标签数 \(\leq 20\)。

【习题】P3254 圆桌问题

【习题】P2765 魔术球问题

  • 有 \(n\) 根柱子,依次进入 \(1,2,\dots m\)。
  • 每个数放的柱子要和当前柱子顶部的数的和是完全平方数。
  • 最大化 \(m\),给构造。
  • \(n\leq 55\)。

【例题】最小链覆盖

  • 给 DAG,求最少的路径覆盖所有点。路径可重点。
  • \(n\leq10^3\)。

传递闭包后跑路径覆盖即可。传递闭包使用 \(\text{bitset}\) 优化做到 \(O(\dfrac{n^3}{\omega})\)。

这道例题可以看做一种偏序关系,定理 \(\text{Dilworth}\) 告诉我们最小链覆盖等于最长反链。举个通俗的例子,最小严格上升子序列覆盖等于最长非严格下降子序列长度。

一些解释:一个 DAG(或偏序集)的反链指最大的点集,点集内两点都不可达。

我们使用 \(\text{Dilworth}\) 定理练习练习。

【例题】P4298 [CTSC2008]祭祀

  • 给 DAG,求反链长度,构造最长反链,问每个点是否可以作为最长反链一点。
  • \(n\leq 100\)。

第一问按照上面来,我们先讲第三问。

对于枚举点 \(i\),我们删掉点 \(i\) 和所有与 \(i\) 有偏序关系的点,如果得到新图的答案恰比原图答案少 \(1\),那么这个点可作为最长反链上一点,否则不行。

第二问我们贪心枚举每一个可以作为最长反链的点,每次选中一个 \(i\) 就把 \(i\) 和和 \(i\) 有偏序关系的点全部删掉就可以了。

给一份参考代码:

#include<iostream>
#include<queue>
using namespace std;
#define ll long long
const int inf=1e9;
namespace graph{/*do sth.*/}
const int MAXN=105;
bool flag[MAXN][MAXN];
int n,m,s,t;
int ls[MAXN],rs[MAXN],tot=0;
bool p1[MAXN],p2[MAXN],chc[MAXN],used[MAXN];
int main(){
s=graph::s=++tot;
t=graph::t=++tot;
cin>>n>>m;
for(int i=1;i<=n;i++)
ls[i]=++tot,rs[i]=++tot;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
flag[u][v]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(flag[i][k]&&flag[k][j])
flag[i][j]=1;//传递闭包
graph::N=tot;
for(int i=1;i<=n;i++)
graph::Add(s,ls[i],1),
graph::Add(rs[i],t,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(flag[i][j])
graph::Add(ls[i],rs[j],1);
int ans=n-graph::max_flow();
cout<<ans<<endl;
for(int k=1;k<=n;k++){
int cnt=0;
graph::init();
for(int i=1;i<=n;i++)
if(flag[k][i]||flag[i][k]||k==i)
p1[i]=1;
else p1[i]=0;
for(int i=1;i<=n;i++)
if(!p1[i])
cnt++,
graph::Add(s,ls[i],1),
graph::Add(rs[i],t,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(!p1[i]&&!p1[j]&&flag[i][j])
graph::Add(ls[i],rs[j],1);
if(cnt-graph::max_flow()==ans-1)
chc[k]=1;
}
for(int i=1;i<=n;i++)
if(chc[i]&&!used[i]){
cout<<"1";
for(int j=1;j<=n;j++)
if(flag[i][j]||flag[j][i]||i==j)
used[j]=1;
}else cout<<"0";
cout<<endl;
for(int i=1;i<=n;i++) cout<<chc[i];
return 0;
}

【例题】[SCOI2007] 修车

  • \(m\) 个师傅修 \(n\) 个车,每个师傅修每个车有不同的时间 \(T_{i,j}\)。
  • 求所有顾客的最小平均等待时间。
  • \(m\leq 9,n\leq 60\)。

不难发现如果一个车 \(c\) 是当前师傅 \(i\) 倒数第 \(k\) 个维修的,那么它对等待时间的贡献就是 \(T_{i,j}\times k\)。

我们考虑转成二分图,左边代表每一个车,右边代表每个师傅的状态(即师傅 \(i\),倒数第 \(j\) 个维修),中间连带权边表示左边的车给右边状态的师傅修的贡献。那么问题就变成了二分图最大匹配最小权。

跑费用流。

【引理】\(\textbf{Konig}\) 定理

二分图最小点集覆盖等于最大匹配。

解释:二分图最小点集覆盖指选一些点使得每条边都有端点是标记点。

证明是显然的,留给读者。(提示,证明两个不等式,即左小等右,右小等左)。

【例题】UVA11419 SAM I AM

  • 给 \(n\times m\) 的方格,有一些标记点。
  • 选择一些行、列使得每个标记点都在标记行或列上。
  • \(n,m\leq10^3\)。

利用 \(\text{Konig}\) 定理转化成二分图最大匹配。

【引理】最大独立集

二分图最大独立集等于总点数减去最小点覆盖

证明:考虑最大独立集为 \(S\)。考虑每一条边,必然不能都是 \(S\) 点,所以 \(V-S\) 是最小点覆盖。

这些结论在二分图当中有一定作用。

【题型】选择限制问题

  • 给一些元素,每个元素选或不选,选有奖励 \(v_i\)。
  • 给一些限制 \(u,v\) 表示 \(u,v\) 不能都选。
  • * 特殊限制(一般是隐藏条件)限制构成二分图。

一般特殊条件难以注意(可能需要染色),如果注意到了就变成了最大独立集问题。

现在点有了权值,我们可以源点向左部点连边的容量是 \(v_i\),右部点向汇点连边的容量为 \(v_i\),原图边容量为 \(\infty\)。

【习题】P3355 骑士共存问题 黑白染色。

【习题】P5030 长脖子鹿放置 行染色。

利用最小割

【引理】最小割

最小割等于最大流

这里最小割的意思是割去最小边权和的边集使得图不连通。

利用最小割原理,我们可以搞些有趣的建模。

【题型】二者取一问题

  • 给 \(n\) 个选择,答案只有 \(0/1\)。两种选择都有不同的奖励权值。
  • 给 \(m\) 个奖励条件,第 \(i\) 条形如如果一个集合的题全部选择 \(0\)(或者 \(1\))。那么额外获得一定的奖励权值。
  • 最大化奖励权值。

先考虑简单情况,也就是没有奖励的时候,可以建出下面这样的图:

我们应该是想要所有边权减去最大流。

所为奖励本质上就是限制。也就是说如果你一个点集没有割完同一方向的边,那么你就要把奖励也给割掉。

也就是说当且仅当你把一边的边全部保留你才能不割奖励,不妨设是限制是全部选 \(0\)。那么可以建图如下:

这里我们可以发现无穷边是不能割的,如果你选择保留了 \(1,2,3\) 之一的 \(1\) 选项,那么就会存在 \(s\to q1\to u\to t\) 的路径,此时必须割掉奖励。

全部选 \(0\) 同理。

【习题】P4313 文理分科 二者取一式问题的简单形式。

【习题】P1361 小M的作物 板子。

【题型】最大权闭合子图

  • 定义有向图 \(G\) 的闭合子图 \(G'\) 为 \(G'\) 中的点在 \(G\) 中的出边终点都在 \(G'\) 当中。
  • 每个点有点权(可能为负)求最大权闭合子图。

建模方式是给源点连向所有正权点,所有负权点连向汇点,流量是点权绝对值。原图正常连边,流量为 \(\infty\)。

考虑最小割,割掉正权点表示不选择该点,割掉负权点表示选择该点,最终的答案为所有正权和减去最小割。

下面证明这一建模:考虑每一条路径 $s\xrightarrow{a} u\xrightarrow{\infty} v\xrightarrow{b} t $。有两种割的方式:

  • 割 \(a\) 留 \(b\):表示不选择 \(u\) 也不选择 \(v\)。
  • 留 \(a\) 割 \(b\):表示选择 \(u\) 也选择 \(v\)。

刚好是闭合子图的形式,剩下还有一种形式:

  • 不选 \(u\) 但选 \(v\)

的形式必然不优,证明完毕。

【习题】P3872 [TJOI2010]电影迷

  • 给有向图,点有点权,边有边权。
  • 求极多边子图,最大化点权减边权。
  • \(n\leq 100\),保证边权为正。
  • 提示:如果边权是 \(\infty\) 就可以转化成最大权闭合子图,可以考虑在最大权闭合子图的建模上改造。

【习题】P2762 太空飞行计划问题 二分图上跑最大权闭合子图。

【习题】 UVA1389 Hard Life 最大密度闭合子图。

  • 提示:二分密度 \(\rho\),所有点权减去 \(\rho\) 跑最大权闭合子图,看权是否 \(>0\) 即可。

拆点法

【例题】P1402 酒店之王

  • \(n\) 个人,每个人有自己喜欢的一些饮料和食物。每种饮料和食物只有一份。
  • 合理安排,最大化饮料食物都喜欢的人数。
  • \(n,p,q\leq100\)。

同上面的建模思路,我们分成三部点,左边时饮料,中间是人,右边是食物。超级源点向饮料连,饮料向喜欢的人连,人向食物连,食物向超级汇点连,所有边流量为 \(1\)。

跑网络流发现一个问题,一个人可能同时被多个食物或饮料匹配上,这是因为每个人也是有限制的。

一般来说我们解决点的限制可以拆点转为边的限制

本题中我们每个人开一个副本,饮料连本体,本体连副本,副本连食物即可。大概可以建出来这样的图

跑网络流。

【例题】P1345 [USACO5.4]奶牛的电信

  • 给定无向图,问最少删多少点才能使 \(c_1,c_2\) 不连通(不能删 \(c_1,c_2\))。
  • \(n\leq 100,m\leq600\)。

我们在利用最小割的时候要清楚哪些边可以割,哪些边不能割。

本题我们主要是研究点,我们先把点拆称 \(l_u,r_u\),超级源点连向 \(l_u\),\(r_u\) 连向超级汇点。由于需要割点,所以 \(l_u\to r_u\) 连容量为 \(1\) 的边,特别的,若 \(u\in\{c_1,c_2\}\) 那么容量也要设为 \(\infty\)。

对于边 \((u,v)\) 来说他是不能割的,所以对 \(r_u\to l_v,r_v\to l_u\) 都设 \(\infty\) 的容量。

跑网络流。

【习题】P3171 [CQOI2015]网络吞吐量

【习题】P3866 [TJOI2009] 战争游戏

平面图最大流问题

【引入】平面图与对偶图

  • 平面图:存在以同构图 \(G\) 使得任意两边仅在节点处相交。
  • 对偶图:把平面图的面试做点,分割两个面的边试做边的图。

举个栗子,下图中原图 \(G\) 和对偶图 \(G'\) 都是平面图,\(G'\) 是 \(G\) 的对偶图。

平面图有个很好的性质,就是平面图的最小割等于对偶图的最短路。

拿上面的图来说,我们要求 \(2,5\) 的最小割,可以建下面这样的对偶图:

对偶图有两个外部点,作为起点和终点,是把直线 \(2,5\) 连出来把平面分成两部分而成的。最短路最小性显然。

【例题】P2046 [NOI2010] 海拔

  • 给一个 \(n\times n\) 的网格图,每条边沿南北或东西。你知道每条边两个方向的客流量。
  • 把 \(n\times n\) 个节点黑白染色,要求 \((1,1)\) 染黑,\((n,n)\) 染白。
  • 最小化从黑节点进入白节点的客流量。
  • \(n\leq 500\)。

标准的平面图,显然的是黑白两个色块必然连续,也就是我们必然可以在对偶图上找到一条路径分开 \((1,1),(n,n)\) 且路径上所有边在平面图中所对应的都是黑白边。

下图是一个参考的例子。

【习题】P4001 [ICPC-Beijing 2006] 狼抓兔子

给流赋予意义

在上面的例子中,网络流当中的“流”似乎只是一种工具,下面介绍几道把“流”视作为选择等有意义的事物的题目。

【例题】P3980 [NOI2008] 志愿者招募

  • 给你 \(m\) 种段,每种段形如 \([l_i,r_i]\),代价是 \(w_i\)。每种段都有无限个。
  • 选代价最小的段使得每个位置 \(i\) 的段数都不小于 \(a_i\)。
  • 保证有解。
  • \(n\leq10^3,m\leq10^4\)。

我们把“流”看做每个人的选取情况

当你想不到什么好的做法时不如想想万能做法网络流。

本题当中每一个段他都对应一个区间,而且取了一个段就要区间加,这不符合二分图的建图。

我们考虑这事一个 DAG,我们把位置 \(i\) 变成边 \(i\to i+1\),费用应该为 \(0\)。但是这里流量比较奇怪,要的是不小于,我们先放一边,我们应该需要的是费用

每个端 \([l_i,r_i]\) 的建模方式应该是 \(l_i\to r_i+1\),表示跳过一个 \([l_i,r_i]\),流量是 \(\infty\),费用是 \(w_i\)。猜测最终的流量应当是定值 \(c=\infty\)。

我们考虑每个位 \(i\) 来说它必须有 \(a_i\) 个位不经过它,转化一下就变成了最多只能容纳 \(c-a_i\) 个流,这个作为上面的流量即可。

注意,我们这样子建模可能让答案不为 \(c\),需要加上一个超级源点 \(s\to 1\),汇点 \(n+1\to t\)。边权为 \(c\)。

妙妙妙。

【例题】P3358 最长 \(\textbf{k}\) 可重区间集问题

  • 若干开区间,最大化选取的开区间长度之和使得每个位置覆盖区间不超过 \(k\)。
  • \(k\leq n\leq 500\)。

我们把“流”试做为每个线段的选取情况。

先显然的离散化,长度变成权值,假设值域为 \(w\)。

按照上面的套路,我们尝试先建一个长度为 \(w\) 的链表示 \([1,w]\) 每个长度为 \(1\) 的开区间,流量是 \(k\),费用是 \(0\)。

每个给定开区间 \([l,r]\) 给节点 \(l,r\) 连边,流量是 \(1\) 费用是 \(len\)。这样一个流量经过代表选择了这条边,反之亦然。

跑最大费用即可。

【例题】P1251 餐巾计划问题

  • 你要进行 \(n\) 天的营业,第 \(i\) 天会使用 \(r_i\) 的餐巾。
  • 每天结束,你会把用完的餐巾交给快洗店(等待天数 \(d_1\),费用 \(r_1\))或慢洗店(等待天数 \(d_2\),费用 \(r_2\))或留下来等明天。
  • 每天开始你也可以选择以 \(p\) 的价格购入新的餐巾。
  • 最小化价格。
  • \(n\leq 2000\)。

把“流”视为每个餐巾。

每天早上我们要购入一些餐巾,每天早上要向晚上送 \(r_i\) 的餐巾,每天晚上可以给后面 \(d_1,d_2\) 天的早上提供若干餐巾,显然的把每天拆成早上和晚上。

有个小问题,就是发现源点给早上提供的餐巾是 \(\infty\),到了晚上如果流回去某天的早上那么这个流就会经过早晚边两次,这个建图的早晚边是流量瓶颈,这样肯定够不成最大流,换句话说这个模型求出来的最大流是不会换洗的。

这个图的缺点在于流不能从晚上的点流回去早上的点,但是我们发现每个晚上的点需求是固定的 \(r_i\)。我们反过来把晚上的点设为左点,早上的点设为右点,这样每个右点需要给汇点提供 \(r_i\) 流量,左点从源点获得 \(r_i\) 流量,左点流量可以向下传递,右点也可以直接从源点获得 \(\infty\) 流量、费用 \(p\)(购入餐巾),左点 \(i\) 也可以向 \(i+d_j\) 的早晨提供若干费用为 \(v_i\) 的流量(换洗)。建模完成。

int main(){
int ls[MAXN],rs[MAXN],s,t,val[MAXN],tot=0;
int n,p,d1,v1,d2,v2;
cin>>n;
for(int i=1;i<=n;i++) cin>>val[i];
cin>>p>>d1>>v1>>d2>>v2;
s=graph::s=++tot;
t=graph::t=++tot;
for(int i=1;i<=n;i++)
ls[i]=++tot,rs[i]=++tot;
for(int i=1;i<=n;i++){
graph::Add(s,ls[i],val[i],0);
graph::Add(s,rs[i],inf,p);
graph::Add(rs[i],t,val[i],0);
if(i+d1<=n) graph::Add(ls[i],rs[i+d1],inf,v1);
if(i+d2<=n) graph::Add(ls[i],rs[i+d2],inf,v2);
if(i<n) graph::Add(ls[i],ls[i+1],inf,0);
}
graph::N=tot;
cout<<graph::MCMF();
return 0;
}

其他的建模方法

【例题】P2754 [CTSC1999]家园 / 星际转移问题

  • 有 \(m\) 个交通工具每一时刻循环经过若干指定站。(不同的站不超过 \(n+2\) 个)
  • 把 \(k\) 个人从站 \(0\) 运送到站 \(-1\) 的最小需要时间。
  • \(n\leq 13,m\leq 20,k\leq 50\)。

对时间分层建模,我们对于每个站的每个时刻状态建模。

枚举需要达成的时刻 \(T\)(这里可以二分,但是没必要),对于一开始站 \(0\) 在 \(0\) 时刻有 \(k\) 点流量,建模不断让他往时刻大的它本身流动,跑最大流即可。

时间不会超过 \(nk\),时间复杂度是 \(O(n^4k^4)\) 远远跑不满。

【习题】P6054 [RC-02] 开门大吉

  • \(n\) 个题,每题 \(m\) 个选项,每个选项有对应奖励值 \(p_{i,j}\)。
  • 若干限制,要求第 \(u_i\) 题比 \(v_i\) 题选择的编号至少多 \(k_i\)。
  • \(n,m,p\leq 80\)。

提示:这里 \(n,m\) 小得可怜,时间、空间却大的离谱。能不能对每一个题目对应选择的状态看做一个点?

【例题】P2045 方格取数加强版

  • 搞 \(k\) 条从 \((1,1)\) 到 \((n,n)\) 的每次只能向右、下的路径。
  • 求经过点权之和最大(经过多次只算一次)。
  • \(n\leq 50\)。

有个难搞的地方叫做经过多次只算一次。我们介绍一个模型我们叫做流量限制模型,如下:

这种建模跑最大费用的时候,前 \(w\) 的流量肯定会经过费用为 \(c\) 的边,后面的只能进入费用为 \(0\) 的边,目的达成。

本题中对方格分点即可。

【例题】P3163 [CQOI2014]危桥

  • 给一个双向图,一些边是好边,可以经过无穷次,一些是坏边,只能经过 \(2\) 次。
  • 问:小 A、小 B 分别从 \(s_1,s_2\) 出发,终点在 \(t_1,t_2\) 要分别走 \(k_1,k_2\) 个来回,是否可行。
  • \(n\leq 50\),多测。

建图是显然的,如果是好边则连 \(\infty\) 流量,否则连 \(2\) 流量。本题显然是一个多原、汇网络流。

我们先说结论:

  • 对 \(s_1,s_2\) 作为源点,\(t_1,t_2\) 作为汇点,分别拥有 \(2k_1,2k_2\) 的流量;
  • 对 \(s_1,t_1\) 作为源点,\(s_2,t_2\) 作为汇点,分别拥有 \(2k_1,2k_2\) 的流量。

若以上两图都有 \(2(k_1+k_2)\) 的最大流,那么可行;否则不可行。下面给出证明:

若答案有解,那么程序必然判断有解:这是显然的。

若程序判断有解,那么答案有解:考虑两个跑完的图的使用网络图为 \(W_1,W_2\),那么 \(\dfrac{W_1+W_2}2\) 可以得到 \(s_1\to t_1\) 的流量使用情况,\(\dfrac{W_1-W_2}{2}\) 可以得到 \(s_2\to t_2\) 的流量使用情况。如果情况不合法,也就是有一个边,两个网络的使用情况为 \(d_1,d_2\) 则 \(|d_1|+|d_2|\geq 2\)。这种情况取 \(d_2'\) 和 \(d_1\) 同号,数值与 \(d_2\) 相同,那么 \(|d_1+d'_2|\geq 2\) 也就是上述两种情况中有一种不可取,矛盾。故成立。

总结

这个博客的题目量很大,有时间自己写写题。

弱鸡还不会上下界,等一段时间补上下界,这也是为什么这个博客的标题带 (I)

网络流与各种建模(I)的更多相关文章

  1. 【bzoj4177】Mike的农场 网络流最小割

    题目描述 Mike有一个农场,这个农场n个牲畜围栏,现在他想在每个牲畜围栏中养一只动物,每只动物可以是牛或羊,并且每个牲畜围栏中的饲养条件都不同,其中第i个牲畜围栏中的动物长大后,每只牛可以卖a[i] ...

  2. POJ 1637 - Sightseeing tour - [最大流解决混合图欧拉回路]

    嗯,这是我上一篇文章说的那本宝典的第二题,我只想说,真TM是本宝典……做的我又痛苦又激动……(我感觉ACM的日常尽在这张表情中了) 题目链接:http://poj.org/problem?id=163 ...

  3. ACM-ICPC (10/19)

    这两天在看虚树,的确很难理解. 不过大致的思路就是说删掉一些没有用的点,但是仍然保持树的相对结构,树上只有两种点,一个是集合点,和一些LCA,这些LCA是为了保持树的相对结构,才留下的. 具体做法网上 ...

  4. hdu 6126 Give out candies

    hdu 6126 Give out candies(最小割) 题意: 有\(n\)个小朋友,标号为\(1\)到\(n\),你要给每个小朋友至少\(1\)个且至多\(m\)个的糖果.小朋友们共提出\(k ...

  5. 洛谷P2763题解

    吐槽一下:蜜汁UKE是什么玩意?! 题目分析: 观察题面,对于给定的组卷要求,计算满足要求的组卷方案,可以发现这是一道明显的有条件的二分图匹配问题,于是考虑建模. 建一个超级源点,一个超级汇点:源点与 ...

  6. 2019 Multi-University Training Contest 2

    2019 Multi-University Training Contest 2 A. Another Chess Problem B. Beauty Of Unimodal Sequence 题意 ...

  7. Codechef RIN 「Codechef14DEC」Course Selection 最小割离散变量模型

    问题描述 提供中文版本好评,一直以为 Rin 是题目名字... pdf submit 题解 参考了 东营市胜利第一中学姜志豪 的<网络流的一些建模方法>(2016年信息学奥林匹克中国国家队 ...

  8. BZOJ 1458 / Luogu P4311 士兵占领 (上下界最小流 / 直接最大流)

    做法1:上下界最小流 先来一发上下界最小流,思路比较暴力,就是把行和列看作n+mn+mn+m个点,(i,j)(i,j)(i,j)如果能占领就从第iii行向第jjj列连一条边,上界为1下界为0;然后从s ...

  9. HNOI2018思记

    4-13 顺顺利利到了湖南.晚上认真研读cf毒瘤题题解,未果. 发现这里含铁丰富的高温多雨式红土地.以及窗户特别深,总有一圈小阳台的房子,门楣深邃如瞳. 看了一波miaom的ZJOI游记,思考了一下解 ...

  10. UVA10779 Collectors Problem

    题目链接:https://cn.vjudge.net/problem/UVA-10779 前言: 本题是关于姜志豪<网络流的一些建模方法>的笔记. 知识点: 最大流 题意摘抄: \(Bob ...

随机推荐

  1. 从零开始配置 vim(10)——快捷键配置

    之前我们对neovim 进行了基础的配置,这篇主要介绍我比较常用的快捷键配置.到这篇开始我们的配置已经可以为两个大的模块--基础配置和快捷键配置.我们的目录也应该按照模块来进行组织.在正式配置之前让我 ...

  2. vim 从嫌弃到依赖(19)——替换

    之前讨论了关于在vim中使用正则表达式的相关知识能方便的进行搜索,现在在之前的基础之上继续来讨论如何进行替换操作. substitute 简介 substitute 允许我们先查找一段文本并用新的文本 ...

  3. go中的sync.RWMutex源码解读

    读写锁 前言 什么是读写锁 看下实现 读锁 RLock RUnlock 写锁 Lock Unlock 问题要论 写操作是如何阻止写操作的 写操作是如何阻止读操作的 读操作是如何阻止写操作的 为什么写锁 ...

  4. 10.2 调试事件获取DLL装载

    理解了如何通过调试事件输出当前进程中寄存器信息,那么实现加载DLL模块也会变得很容易实现,加载DLL模块主要使用LOAD_DLL_DEBUG_EVENT这个通知事件,该事件可检测进程加载的模块信息,一 ...

  5. Centos7 本地与网络Yum源配置

    虽然Yum工具,只需要你的主机连接互联网,就可以直接使用,但是有些时候我们需要自定义Yum源实现加速,下面我们将配置一个本地Yum源,来使用光盘中的软件,其他网络配置操作步骤相同,只是会在baseur ...

  6. 9、数据库学习规划:Oracle - 学习规划系列文章

    甲骨文公司的Oracle数据库是笔者认为的目前市面上性能最强大的数据库.其版本也发展到了现在的12c,提供的功能也更加的强大了.以前笔者使用的是9i,十几年过去了,也才发展到12代,说明Oracle数 ...

  7. 深空物联网通信中视频流的智能多路TCP拥塞控制|文献阅读|文献分析和学习|拥塞控制|MPTCP|SVC

    前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总https://blog.cs ...

  8. 单片机 IAP 功能基础开发篇之APP升级(一)

    引言 目的 主要介绍单片机 IAP 开发的设计思路,如何不使用下载烧录器的方式对单片机的程序进行升级,升级区域包括 bootloader 和用户程序的升级,升级方式有 UASRT 通信.CAN 通信和 ...

  9. mac os 解决Error: EMFILE: too many open files错误

    壹 ❀ 引 构建项目时终端反复出现Error: EMFILE: too many open files的错误,经排查是因为项目较大,发布过程中已经超过了mac默认的文件监听上限,错误如下: 解决方式也 ...

  10. JS leetcode 翻转字符串里的单词 题解分析

    壹 ❀ 引 今天来做一道难度中等,但实际难度并不是很高的题目,题目来源leetcode151. 翻转字符串里的单词,题目描述如下: 给定一个字符串,逐个翻转字符串中的每个单词. 示例 1: 输入: & ...