是学了忘忘了学学了再忘的网络流()
最大流
只会 Dinic。
[SHOI2011]银行家
企图思考取钱顺序问题在建图上怎么体现未果。
转化题意:一个人打开了他能打开的所有保险箱(设为集合 S S S),相当于 S S S 内所有金币能在这些保险箱和人之间自由流动。不妨令所有金币都给人,下一个人打开保险箱就可以看作从上一个人手里拿金币,拿到的可以是 S S S 内的所有金币,所以连容量为 i n f inf inf 的边。保险箱和人再分别向超级源点和超级汇点连边就做完了。
[CQOI2014]危桥
神仙题
往返变成去两次,建出大概的图很简单,然后被双向边正反最多一共走两次的限制卡住了,甚至在看见第一篇题解之前没想过
a
1
a1
a1 可能把部分流量传给
b
2
b2
b2 这件事。
很巧妙的做法:交换 b 1 b1 b1 和 b 2 b2 b2,跑两次最大流,两次都满流才判合法。
假设正向跑最大流的时候某个危桥正反通行次数加起来超过限制,那么反向跑的时候, b 1 → b 2 b1 \to b2 b1→b2 的通行方向变为正向,和 a 1 → a 2 a1 \to a2 a1→a2 会一起被正向容量限制,被判为不合法。
对于我们不想要的形如 a 1 → b 2 a1 \to b2 a1→b2 的流量,在正图上 a 1 a1 a1 能给 b 2 b2 b2 x x x 的流量,那 a 1 a1 a1 就给了 a 2 a2 a2 a n − x an-x an−x 的流量,反图上 a 1 → a 2 a1 \to a2 a1→a2 流量一定可以保持不变,那 b 2 → a 2 b2\to a2 b2→a2 就有 x x x 的流量。根据无向图的性质, a 1 → a 2 a1\to a2 a1→a2 也能有 x x x 的流量,故合法。
最小割
最小割就是把一张网络图通过断一些边划分成两个集合所需要的最小代价。跟最大流没什么关系,但由于其在数值上等于最大流,于是还是 Dinic。
最小割不一定唯一,若想求出最小割的最少割边数目,可以使用奇技淫巧跑一遍 Dinic 求出: n n n 个点 m m m 条边的图,设定常数 b > m b>m b>m,令每一条边的 w = w × b + 1 w=w\times b+1 w=w×b+1,求出最大流 a n s ans ans,实际最大流即为 a n s / b ans/b ans/b,最小割最少边数即 a n s m o d b ans\mod b ansmodb。
小M的作物
以点全部在集合 S S S 内为例,要处理指定点在一个集合内产生的贡献,首先肯定不能这一组点之间互相连边,就只能建一个虚拟节点 v v v,连接 s → v s\to v s→v,容量为额外贡献 c c c。如果指定的任意一个点不在集合 S S S 内,那求的最小割就要强制断开这条边。于是考虑由 v v v 向每个指定点 x x x 连容量为 i n f inf inf 的边,表示这条边永远不会被割断,那么每次断的就只能是容量为 c c c 的边了,满足题意。
最小割求的是减少贡献的最小值,所以答案要拿总权值(当然不算 i n f inf inf 边)减最小割求解。
先是没计算准边数 RE 飞了,然后 Dinic 实现太差 T 飞了()dfs 的时候如果还有剩余流量没用说明当前点已经满流了,要标记 d e p [ x ] = − 1 dep[x]=-1 dep[x]=−1 啊。
太空飞行计划问题
先转化一波:所求 = 选择的正权点权值和 - 选择的负权点权值和 = 所有正权点权值和 - 未选择正权点权值和 - 选择的负权点权值和。
对于点权有正有负且选择有依赖关系的,将正权点与 s s s 相连,负权点与 t t t 相连,依赖关系用容量为 i n f inf inf 的边描述。这样找最小割划分集合的时候,不选正权点即它与 s s s 的连边断开,计入最小割;选负权点即它与 t t t 的连边断开,计入最小割。发现最小割就是我们上面的柿子里后面减的两项。
方案输出很神秘,由于不确定划分是否将 s s s 和 t t t 划分到了一个集合等原因,拿边的容量判是错的,应该利用 bfs 中被指定层数的点都和s在同一集合中判断。
最小割唯一性判定
先跑一遍最大流,那么割边一定都满流。去除满流的边,分别从 s s s 和 t t t 查找能到达的点,记点数分别为 c 1 c1 c1 和 c 2 c2 c2,若 c 1 + c 2 = n c1+c2=n c1+c2=n 则最小割唯一,否则不唯一。
调了 100 年的 AC 代码(为什么 zoj 时限 5s 到了 ybt 就剩 1s 了啊/fn:
bool v1[N],v2[N];
inline int get(int x){
int qaq=x&3;
if(qaq<=1) return x-2;
else return x+2;
}
void dfs1(int x){
v1[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].v,w=e[i].w;
if(w&&!v1[v]) dfs1(v);
}
}
void dfs2(int x){
v2[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].v,w=e[i].w;
if(e[get(i)].w&&!v2[v]) dfs2(v);
}
}
signed main(){
while(1){
n=read(),m=read(),ss=read(),tt=read();
if(!n&&!m) return 0;
cnt=1,s=n+1,t=n+2;
ff(i,1,t) head[i]=v1[i]=v2[i]=0;
ff(i,1,m){
int u=read(),v=read(),w=read();
add(u,v,w),add(v,u,w);
}
add(s,ss,2e9),add(tt,t,2e9);
while(bfs()) dfs(s,2e9);
v2[s]=v1[t]=1;
dfs1(s),dfs2(t);
// for(int i=2;i<=cnt;i+=2) cout<<e[i].u<<' '<<e[i].v<<' '<<e[i].w<<endl;
ff(i,1,n) if(!v1[i]&&!v2[i]){puts("AMBIGUOUS");goto aaa;}
puts("UNIQUE");
aaa: ;
}
}
[YBTOJ]矛盾指数
非常神秘题。
自己考虑了一会儿固定分母求最大分子,没想出来,然后就不会了。
考虑二分答案 m i d mid mid,进行一个柿子的推: c n t 边 c n t 点 ≥ m i d ⇒ c n t 边 − m i d × c n t 点 ≥ 0 \dfrac{cnt_边}{cnt_点}\ge mid\Rightarrow cnt_边-mid\times cnt_点\ge 0 cnt点cnt边≥mid⇒cnt边−mid×cnt点≥0。
然后就是最大权闭合图问题了。 c h e c k check check 的时候若跑出来的最小割等于边数 m m m,便显然不合法。据此可以得到最大矛盾指数。
然后在最大矛盾指数对应的图上用 bfs 后得到的 d e p dep dep 得到方案即可。
费用流
依然是只会 Dinic 战士
以下用 ( w , c ) (w,c) (w,c) 表示容量为 w w w,费用为 c c c 的边。
[国家集训队]航班安排
被变量重名搞炸了写不下去了/fn
但对于网络流小白来说建图方法还是很值得总结的qaq
发现机场作为点不可做,考虑任务作为点。一个源点表示基地, m m m 个点表示任务,对于能在开始时从基地飞到出发地的任务,连边 ( i n f , − 空载飞行费用 ) (inf,-空载飞行费用) (inf,−空载飞行费用) 即可。同样地可以连出表示执行完任务回到基地的边,改为连到汇点 t t t 就好。大概框架就建完了。
单次任务收益没地方放,考虑拆点,即每个任务用两个点表示,中间连边 ( 1 , c ) (1,c) (1,c),其中 1 1 1 表示一个任务最多接一次。这样收益就有地方放了。
任务执行时间的限制怎么办?注意到 m m m 很小,两两枚举任务,如果上一个任务执行完可以赶到下一个任务地,就在两个任务之间连边 ( i n f , − 空载飞行费用 ) (inf,-空载飞行费用) (inf,−空载飞行费用)。
现在题里变量只差一个 k k k 架飞机的限制。在基地前再接一个源点与其连边 ( k , 0 ) (k,0) (k,0) 即可。
[NOI2008] 志愿者招募
以时间轴为链建图,很神秘。
至少 a i a_i ai 个人上班不好表示,考虑把它变成至多 i n f − a i inf-a_i inf−ai 个人放假,就可以用最大流了。
于是在 i i i 和 i + 1 i+1 i+1 之间连边 ( i n f − a i , 0 ) (inf-a_i,0) (inf−ai,0) 表示第 i i i 天的限制,特别地,在 s s s 和 1 1 1 之间连边 ( i n f , 0 ) (inf,0) (inf,0) 表示初始所有人都不上班,形成原始链。对于每一类志愿者,在 s t st st 和 e d + 1 ed+1 ed+1 之间连边 ( i n f , c ) (inf,c) (inf,c) 即可。这样能在原始链上走的流量一定会选择接着放假,不得不上班的才会走带花费的边,符合问题要求。
[AGC031E] Snuke the Phantom Thief
发现无法同时表示被 L 限制和被 R 限制,于是……枚举!
枚举偷宝石的个数 k k k,转化限制: x ≤ a x\le a x≤a 的至多选 d d d 个 ⇒ \Rightarrow ⇒ 第 d + 1 d+1 d+1 个满足 x ≥ a + 1 x\ge a+1 x≥a+1,这样就可以知道每个点是否可以做按 x x x / y y y 排序的第 i i i 个。
于是可以建两排点表示按 x x x / y y y 排序选的 k k k 个点,分别向源点汇点连容量为 1 1 1 的边。再把 n n n 个点拆成两排,对应点之间连边 ( 1 , v i ) (1,v_i) (1,vi) 表示限流,根据预处理出的每个点是否可以做按 x x x / y y y 排序的第 i i i 个点的信息再连边,跑最大费用最大流即可。
有上下界网络流
有源汇上下界可行流是 T T T 到 S S S 附加边流量!!
Captain America
水黑好评pap
显然最少花费就是要选最多花费较小的颜色,变成了最大流问题。
考虑二分图形建图,一边表示行,一边表示列。对于每一个限制,假设 c n t cnt cnt 为当前行/列上有几个点,限制可以转化为选的点数 ∈ [ ⌈ c n t − d 2 ⌉ , ⌊ c n t + d 2 ⌋ ] \in[\left\lceil\frac{cnt-d}{2}\right\rceil,\left\lfloor\frac{cnt+d}{2}\right\rfloor] ∈[⌈2cnt−d⌉,⌊2cnt+d⌋],就是在表示当前行/列的点和 s / t s/t s/t 之间连边。对于每一个点,就在它所在行和所在列之间连容量为 1 1 1 的边即可。然后跑有源汇上下界最大流就行了。
线性规划对偶
估计退役前不会证明,于是来记个结论。
s . t . { ∀ i , x a i − x b i ≥ c i x i ≥ 0 s.t.\begin{cases}\forall i,x_{a_i}-x_{b_i}\ge c_i\\x_i\ge 0\end{cases} s.t.{∀i,xai−xbi≥cixi≥0
最大化 ∑ c i x i \sum c_ix_i ∑cixi
对于每对限制,连接 ( a i , b i , + ∞ , c i ) (a_i,b_i,+\infty,c_i) (ai,bi,+∞,ci)。
对于 k i > 0 k_i>0 ki>0,连接 ( s , i , [ 0 , k i ] , 0 ) (s,i,[0,k_i],0) (s,i,[0,ki],0) 和 ( i , t , [ 0 , + ∞ ) , 0 ) (i,t,[0,+\infty),0) (i,t,[0,+∞),0)。
对于 k i < 0 k_i<0 ki<0,连接 ( i , t , [ − k i , + ∞ ) , 0 ) (i,t,[-k_i,+\infty),0) (i,t,[−ki,+∞),0)。
然后理论上需要跑上下界最大费用最大流。不会。
很多题存在一个特殊性质: ∑ k i = 0 \sum k_i=0 ∑ki=0,这时发现 s s s 连接的边流量之和上界 = = = t t t 连接的边流量之和下界,连边可简化为: k i > 0 k_i>0 ki>0 时 ( s , i , k i , 0 ) (s,i,k_i,0) (s,i,ki,0), k i < 0 k_i<0 ki<0 时 ( i , t , − k i , 0 ) (i,t,-k_i,0) (i,t,−ki,0),就是朴素的费用流了。