『Record』网络流

经典模型总结

最小割点

即问题求解删点的贡献为 \(w_i\) 下最小割。

经典拆点,考虑将点拆成 \(u_{in},u_{out}\),连 \(u_{in} \xrightarrow{w_u} u_{out}\)

而对于原图边 \((u,v)\),连 \(u_{out} \xrightarrow{+\infty} v_{in}\)

集合划分模型

有一个经典的模型是这样的,给定 \(E,c,a,b\),求解:

\[\min_{x[1,2,\dots,n]} \{\sum\limits_{(u,v)\in E} x_u \overline{x_v}c_{u,v}+\sum\limits_{i=1}^{n} \left( x_ia_i+\overline{x_i}b_i \right) \} \]

其中 \(x_i=0 /1\),而 \(\overline{x_i}\) 为取反操作。

考虑建模,首先对于任意 \(i\) ,连 \(S \xrightarrow{a_i} i \xrightarrow{b_i} T\)

对于 \((u,v) \in E\),连 \(u \longleftrightarrow v\) 流量为 \(c_{u,v}\)

一些奇怪的问题:

  • 对于存在 \(a_i,b_i\) 有负数的情况,考虑由于 \(a_i,b_i\) 必定会有一个被选择,则考虑给 \(a_i,b_i\) 同时加上一个极大值,最后贡献减去即可。

  • 对于要求限制为 \(x_u=0 \ \text{and} \ x_v=1\) 对式子有 \(c_{u,v}\) 的贡献,此时连单向边即可。

有个变式:

\[\max_{x[1,2,\dots,n]} \{\sum\limits_{(u,v)\in E} \left(x_ux_vc_{u,v} +\overline{x_u} \overline{x_v}c_{u,v}\right) +\sum\limits_{i=1}^{n} \left( x_ia_i+\overline{x_i}b_i \right) \} \]

同理的,不过考虑割掉的边表示不选:

\(S \xrightarrow{c_{i,j}} (i,j),S \xrightarrow{a_i} i,S \xrightarrow{a_j} j\)

\((i,j)^{\prime}\xrightarrow{c_{i,j}} T,i \xrightarrow{b_i} T ,j \xrightarrow{b_j} T\)

\((i,j) \xrightarrow{+\infty} i,(i,j) \xrightarrow{+\infty} j,i \xrightarrow{+\infty} (i,j)^{\prime},j\xrightarrow{+\infty} (i,j)^{\prime}\)

这是不难理解的,可以手玩一下。

最大权闭合子图

只讲建模,不讲证明,因为容易手玩。

\(S \xrightarrow{a_i} i,a_i\geq 0\)

\(i \xrightarrow{-a_i} T,a_i< 0\)

\(u \xrightarrow{+\infty} v,(u,v) \in E\)

答案即为 \(\sum\limits_{a_i\geq 0} a_{i}-c\),其中 \(c\) 为最小割。

有负环的费用流

考虑类比上下界费用流,先转化为无源汇,考虑钦定负费用边必定流满,此处运用到网络流反悔的特殊性质。

同时删除原边,加入反边,将负费用取去绝对值,最后考虑加入虚源汇点,同理上下界建模即可。

上下界只是应定流满下界,而此处只是应定直接流满,但是又不能直接跑费用流,因为仍然存在负环,所以直接考虑建出反边跑。

注意,要在虚源汇,实源汇都跑一遍,这个和上下界最大/小流同理。

最大费用最大流

费用取反,然后跑有负环的费用流即可。

对偶问题

对于有些问题,限制在点上,而贡献在边上,可以考虑使用网络流解决。

而对于限制在边上,而贡献在点上的问题,大多数不好建模,考虑通过线性规划对偶解决。

这类问题没有统一的分析方式,对于不同的题目将有不同的求法。

而对偶则是指求解一类形如如下的问题:

\[\min \sum\limits_{i=1}^{n} a_i x_i \]

\[\forall i \in [1,m] \sum\limits_{j=1}^{n} c_{i,j} x_j \leq b_i \]

这类问题等价于求解:

\[\max \sum\limits_{i=1}^{m} b_i y_i \]

\[\forall j \in [1,n] \sum\limits_{i=1}^{m} c_{i,j} y_i \leq a_j \]

但是对偶后构造方案大多数情况比较麻烦,可能需要具体到题目。

网络流24题

P1251 餐巾计划问题

拆点,分别维护干净的毛巾和脏的毛巾。

建模如下:

\(S \xrightarrow{(r_i,0)} i,i^{\prime} \xrightarrow{(r_i,0)} T,S \xrightarrow{(+\infty,p)} i^{\prime}\)

\(i \xrightarrow{(+\infty,f)} (i+m)^{\prime},i \xrightarrow{(+\infty,s)} (i+n)^{\prime}\)

如果用了原始对偶优化复杂度就是 \(O(n^2+fn\log n)\)

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

飞船停靠站点与时间挂钩,考虑运用分层图思想。

枚举答案 \(t\),建模:

\((x,y)\) 表示时间在 \(x\) 的点 \(y\)

\(S \xrightarrow{+\infty} (i,0),(i,j) \xrightarrow{+\infty} (i+1,j)\)

\((i,u) \xrightarrow{h_j} (i+1,v)\) ,其中 \(u,v\)\(i\) 时刻到 \(i+1\) 时刻 \(j\) 号飞船的边上两点。

可以在答案增量的后加边然后在残余网络上跑最大流。

复杂度?。

P2756 飞行员配对方案问题

二分图最大匹配模板。

P2761 软件补丁问题

一个bug 有两种状态,出现或者没出现,状压每种状态,然后 dij 转移即可。

P2762 太空飞行计划问题

将实验和仪器都看成是点,一个实验向需要的一起连边,就是一个最大权闭合子图问题。

P2763 试题库问题

经典建模:

\(S \xrightarrow{1} i,i\xrightarrow{1}n+j,n+i\xrightarrow{p_i} T\)

其中 \(i\xrightarrow{1}n+j\) 是第 \(i\) 个试题选择 \(j\) 类型。

构造方案判流满否。

P2764 最小路径覆盖问题

DAG 不交最小链覆盖。

拆入点和出点,对于原图中边 \((u,v)\)\(u_{out} \xrightarrow{+\infty} v_{in}\)

答案即为 \(n-m\),其中 \(n\) 为点数,\(m\) 为最大匹配。

扩展:

  • Dilworth 定理,偏序集中,最长反链等于最小链覆盖。

  • DAG 有交最小链覆盖,按照以传递闭包建出的邻接表求不交最小链覆盖即为所求。

  • 二分图中的最小点覆盖等于最大匹配。

  • 二分图中最小边覆盖覆盖等于最大独立集等于点数减去最小点覆盖。

P2765 魔术球问题

枚举答案 \(i\) ,对于 \(i+j=a^2(i>j)\) ,由于从小到大放,连 \(i\rightarrow j\),求最小链覆盖若超过 \(n\)\(i-1\) 为答案。

构造方案同上。

P2766 最长不下降子序列问题

第一问水。

不能发现一个性质,设 \(f_i\) 表示以 \(i\) 为结尾的 LIS 长度。在我选择的子序列作为答案中,其中的一个长度为 \(s\) 的子序列上,两个相邻的元素 \(x,y\) 必然满足\(f_x=f_y+1(y<x)\)

那么可以将问题转化为此类 \(x,y\)\(y \rightarrow x\),求最多不交 \(k\) 长链,同时每个点 \(i\) 只能最为链上第 \(p_i\) 个。

建模:

\(S \xrightarrow{1} i(f_i=1),i\xrightarrow{1}j(f_j=f_i+1,a_i\leq a_j),i\xrightarrow{1} T(f_i=s)\)

跑最大流即为所求。

第三问同理的,只是 \(S \xrightarrow{+\infty} 1,n \xrightarrow{+\infty} T(f_n=s)\)

注意特判 \(n=1\)

P2770 航空路线问题

不难发现要求即为 \(1_{out}\)\(n_{in}\) 的度数 \(\leq 2\),其余点度数 \(\leq 1\)

直接建模跑最大费用最大流即可,由于没环,直接反权值跑即可。

构造方案直接看是否满流即可。

P2774 方格取数问题

黑白染色,直接求解二分图最大权独立集即可。

P3254 圆桌问题

二分图多重匹配模板题。

类比二分图最大匹配的建模方式:

\(S \xrightarrow{r_i} i,i \xrightarrow{1} j+n,j+n \xrightarrow{c_j} T\)

P3355 骑士共存问题

黑白染色,互相攻击点连边,跑二分图最大独立集即可。

P3356 火星探险问题

拆点 \(in,out\),点权转边权,对于有石子的点 \(u_{in} \xrightarrow{(1,1)} u_{out},u_{in} \xrightarrow{(n-1,0)} u_{out}\)

对于没有石子的点 \(u_{in} \xrightarrow{(n,0)} u_{out}\)

没环,最大费用最大流,输出方案记录一个点向下右的流量。

P3358 最长 k 可重区间集问题

这题放在 P3357 之前,原因是可以理解为后者是前者的加强版。

考虑用将限制转化,考虑一个点可以至多被 \(k\) 个区间交,那么考虑给 \(k\) 个区间分组,每组中区间无交则为充要条件。

应定 \(k\) 组可以直接建模设置流量上界 \(k\),而现在是容易建模的,拆区间为两点 \(L_{i},R_{i}\),设其左右端点为 \(l_i,r_i\)

\(S \xrightarrow{(1,0)}L_i,R_i\xrightarrow{(1,0)}T,T\xrightarrow{(k,0)} T^{\prime},L_i \xrightarrow{(1,r_i-l_i)}R_i\)

\(R_{i} \xrightarrow{(1,0)}L_j(r_i\leq l_j)\)

这样建模 \(m=O(n^2)\) 直接跑肯定不够优秀。

考虑优化:

不妨将所有 \(L_i,R_i\) 离散化拍到数列 \(x={1,\dots,p}\) 上。

建模:

\(S \xrightarrow{(k,0)}1,p \xrightarrow{(k,0)}T,L_i \xrightarrow{(1,r_i-l_i)}R_i\)

\(i \xrightarrow{(k,0)} i+1(i\in[1,p))\)

\(m=O(n)\),最大费用最大流 \(O(nmk)\)

P3357 最长 k 可重线段集问题

可能发现就是 \(x\) 维做最长 \(k\) 可重区间集。

和 P3358 不同的是当存在一个线段平行于 \(y\) 轴,则其为 \([x,x]\) 闭区间。

考虑拆点,\(x_{in},x_{out}\)

建模:

\(S \xrightarrow{(k,0)}1_{in},p_{out} \xrightarrow{(k,0)}T,L_{i_{out}} \xrightarrow{(1,w_i)}R_{i_{in}}(L_i\not=R_i),L_{i_{in}} \xrightarrow{(1,w_i)}R_{i_{out}}(L_i=R_i)\)

\(i_{in} \xrightarrow{(k,0)} i_{out}(i\in[1,p])\)

\(i_{out} \xrightarrow{(k,0)} (i+1)_{out}(i\in[1,p))\)

\(m=O(n)\),最大费用最大流 \(O(nmk)\)

P4009 汽车加油行驶问题

和网络流没啥关系。

\(f_{i,j,k}\) 表示走到 \((i,j)\) 剩下 \(k\) 个可行走单位,dij 暴力转移即可。

P4011 孤岛营救问题

由于钥匙数量很少,状压 \(O(nm 2^{P})\)

P4012 深海机器人问题

类比 P3356 即可。

P4013 数字梯形问题

经典拆点,点边容量为 \(1\) 表示点有限制不交,边间容量为 \(1\) 表示边有限制不交,否则容量赋为 \(+\infty\)

然后跑最大费用最大流即可,无环。

P4014 分配问题

二分图最小/大权匹配。

P4015 运输问题

同上。

P4016 负载平衡问题

类似上下界网络流,算出 \(avg\),若 \(a_i<avg\),连 \(S\xrightarrow{(avg-a_i,0)}i\),若 \(a_i>avg\),连 \(i\xrightarrow{(a_i-avg,0)}T\)

相邻边连容量为 \(+\infty\) 费用为 \(1\) 的边。

例题

[P2805 NOI2009] 植物大战僵尸

考虑将保护关系表示为,选什么之前必定要选什么,这和选一个点之前一定要右侧点限制同理。

如果选 \(u\) 前必须选 \(v\) 那么连 \(u \rightarrow v\),然后将环去掉后跑最大权闭合子图即可。

P5192【模板】有源汇上下界最大流

模板题?

建模:

\(S \xrightarrow{[0,D_i]} i,i\xrightarrow{[L_{i,k_i},R_{i,k_i}]} n+k_i,n+i \xrightarrow{[G_i,+\infty]} T\)

直接跑上下界最大流即可。

Florida

题意:给定一个有向图,\(n\) 个点,\(m\) 条边,你需要给每个点定权值 \(val_u \in[1,n]\),对于每条边 \(u \rightarrow v\),满足 \(val_u<val_v\),最小化 \(\sum\limits_{(u,v) \in E} val_v-val_u\)\(n \leq 300,m \leq 1500\)

首先把已知限制写出来:

\[\min \sum\limits_{i=1}^{n} (in_i-out_i) val_i \]

\[\forall (u,v) \in E, a_v-a_u\geq 1 \]

注意这是一个对偶形式,限制在边上,而贡献在点,难搞。

考虑对偶过去,把限制拍到点上,贡献搞到边上后尝试网络流解决。

对偶:

\[\max \sum\limits_{i=1}^{m} b_i \]

\[\forall i \in [1,n],\sum\limits_{e=(u,i)} b_e-\sum\limits_{e=(i,u)} b_e \leq in_i-out_i \]

类似运用上下界的思路,考虑补流,在补流后的图上跑最大费用最大流,这就是答案。

对于构造方案,原限制类似差分约束,对于一条被流过的边,根据互补松弛性定理可以得出,这条边对应的原问题限制一定满足 \(val_v-val_u=1\),按照上述所有限制建差分约束模型即可求得一组解。

#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#pragma GCC optimize("Ofast","unroll-loops","inline")
#include<bits/stdc++.h>
#define ll long long
//#define int ll
#define pb push_back
#define pii pair<int,int>
#define MP make_pair
#define v first
#define w second
using namespace std;
const int N=2e5+20,M=2e5+20,mod=998244353,Inf=1e9;
struct Edge{int v,c,w;};
namespace mcmf{
	int n,s,t,ecnt,cur[N],d[N];bool vis[N];
	Edge g[M];
	vector<int> e[N];
	inline void init(){for(int i=0;i<=n;i++) e[i].clear();n=s=t=0;ecnt=1;}
	inline void add(int u,int v,int c,int w){
		g[++ecnt]=(Edge){v,c,w};e[u].pb(ecnt);
		g[++ecnt]=(Edge){u,0,-w};e[v].pb(ecnt);
	}
	queue<int> q;
	inline bool spfa(){
		for(int i=0;i<=n;i++) d[i]=-Inf;d[t]=-Inf;
		vis[s]=1;d[s]=0;q.push(s);
		while(!q.empty()){
			int u=q.front();q.pop();
			vis[u]=0;
			for(int x:e[u]){
				Edge eg=g[x];
				if(eg.c<=0||d[eg.v]>=d[u]+eg.w) continue;
				d[eg.v]=d[u]+eg.w;
				if(!vis[eg.v]) q.push(eg.v),vis[eg.v]=1;
			}
		}
		return d[t]!=-Inf;
	}
	int dfs(int u,int a){
		if(u==t||!a) return a;
		vis[u]=1;
		int flow=0,f;
		for(int &i=cur[u];i<(int)e[u].size();i++){
			Edge eg=g[e[u][i]];
			if(eg.c<=0||d[eg.v]!=d[u]+eg.w||vis[eg.v]) continue;
			if((f=dfs(eg.v,min(eg.c,a)))){
				a-=f;flow+=f;g[e[u][i]].c-=f;g[e[u][i]^1].c+=f;
				if(!a) break;
			}
		}
		vis[u]=0;
		return flow;
	}
	int solve(){
		int res=0;
		while(spfa()){
			for(int i=0;i<=n;i++) cur[i]=0;
			res+=dfs(s,Inf)*d[t];
		}return res;
	}
}
vector<pii> G[N];
vector<int> edge[N];
int n,m,in[N],out[N],deg[N],dis[N];bool vis[N];
queue<int> q;
void solve(){
	mcmf::init();
	for(int i=1;i<=n;i++) in[i]=0,out[i]=0,deg[i]=0,edge[i].clear(),G[i].clear();
	cin>>n>>m;mcmf::n=n+1;mcmf::s=0;mcmf::t=n+1;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		mcmf::add(u,v,Inf,1);
		edge[u].pb(v);
		in[v]++;out[u]++;deg[v]++;
	}
	for(int i=1;i<=n;i++) if(!deg[i]) q.push(i);
	int tot=0;
	while(!q.empty()){
		tot++;
		int u=q.front();q.pop();
		for(int v:edge[u]){
			if(!--deg[v]) q.push(v);
		}
	}
	if(tot!=n) return cout<<"NIE\n",void();
	for(int i=1;i<=n;i++){
		if(in[i]<out[i]) mcmf::add(mcmf::s,i,out[i]-in[i],0);
		if(in[i]>out[i]) mcmf::add(i,mcmf::t,in[i]-out[i],0);
	}
	int res=mcmf::solve();
	for(int v=1;v<=n;v++){
		for(int x: mcmf::e[v]){
			Edge eg=mcmf::g[x];int u=eg.v,w=eg.w;
			if(w<0&&u<=n){
				if(eg.c) G[u].pb(MP(v,1));
				G[v].pb(MP(u,-1));
			}
		}
	}
	for(int i=1;i<=n;i++) dis[i]=1,vis[i]=1,q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0; 
		for(pii x:G[u]){
			if(dis[x.v]>dis[u]+x.w){
				dis[x.v]=dis[u]+x.w;
				if(!vis[x.v]) q.push(x.v),vis[x.v]=1;
			}
		}
	}
	int mn=Inf;
	for(int i=1;i<=n;i++) mn=min(mn,dis[i]);
	for(int i=1;i<=n;i++) dis[i]-=mn-1;
	cout<<res<<'\n';
	for(int i=1;i<=n;i++) cout<<dis[i]<<' ';cout<<'\n'; 
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--) solve();
	return 0;
}
posted @ 2023-10-27 15:11  Detect-Perplexity  阅读(10)  评论(0编辑  收藏  举报