[学习笔记]网络流·最小割题目选讲

本文详细介绍了网络流中的最小割概念,并通过10道BZOJ题目,展示了最小割在互斥选择模型中的应用。文章涵盖投票、旅行、矩阵运算等多个场景,通过转化问题并建立图模型,利用最大流等于最小割的定理求解问题的最小代价或最大收益。
摘要由CSDN通过智能技术生成
  • 已经 2 周没写博客了
  • 这也是 2019 的第一篇博客,一个新的开始

引入

  • 最小割的概念:给定一个图,含源点 S S S 和汇点 T T T ,每条有向边有容量
  • 选出一些边删掉,使得没有从 S S S T T T 的路径
  • 求选出的边的容量和的最小值
  • 最大流和最小割都是线性规划问题中的一类
  • 最小割可以看成是最大流的对偶问题
  • 定理:最大流 = = = 最小割
  • 证明
  • 显然最小割不能小于最大流
  • 我们知道,跑完最大流之后,残余网络中没有从 S S S T T T 的路径
  • 图被分为两个集合
  • 根据流量平衡条件得到,流出 S S S 所在集合的流量等于流入 T T T 所在集合的流量
  • 而横跨 S S S 所在集合和 T T T 所在集合的所有边构成一个割,且等于最大流
  • 于是这个割就是最小割
  • 证毕
  • 最小割在题目中最常见的模型就是互斥选择模型(有这个模型更好的定义请在评论区指出)
  • 例如: n n n 个变量,每个变量取 0 0 0 有一定代价,取 1 1 1 有一定代价,外加 m m m 个约束 ( i , j , x ) (i,j,x) (i,j,x) ,表示 i i i j j j 取不同值有额外代价 x x x ,求最小代价和
  • 下面我在 BZOJ 上选出了 10 10 10 道最小割题
  • 10 10 10 道题都是互斥选择的模型

(1)[BZOJ1943][Shoi2007]Vote 善意的投票

题意

  • 即为引入中举的栗子
  • n n n 个变量,第 i i i 个变量为 x i x_i xi
  • x i x_i xi 取值等于 a i a_i ai 时不产生代价,否则产生 1 1 1 的代价
  • m m m 个二元组 ( i , j ) (i,j) (i,j)
  • 表示 ( i , j ) (i,j) (i,j) 取值不同则会产生 1 1 1 的代价
  • 求最小代价和
  • 2 ≤ n ≤ 300 2\le n\le300 2n300

做法

  • 源点 S S S 汇点 T T T
  • 对于所有的 1 ≤ i ≤ n 1\le i\le n 1in ,对第 i i i 个变量 x i x_i xi 建点 i i i
  • 如果 a i = 0 a_i=0 ai=0
  • 则由 S S S i i i 连容量为 1 1 1 的边
  • i i i T T T 连容量为 0 0 0 的边
  • 否则由 S S S i i i 连容量为 0 0 0 的边
  • i i i T T T 连容量为 1 1 1 的边
  • 这时候边 &lt; S , i &gt; &lt;S,i&gt; <S,i> &lt; i , T &gt; &lt;i,T&gt; <i,T> 显然有且仅有一条边被割掉
  • 可以看成如果保留 &lt; S , i &gt; &lt;S,i&gt; <S,i> x i x_i xi 0 0 0
  • 否则 x i x_i xi 1 1 1
  • 考虑处理变量间的影响
  • 对于二元组 ( i , j ) (i,j) (i,j) ,连边 &lt; i , j &gt; &lt;i,j&gt; <i,j> &lt; j , i &gt; &lt;j,i&gt; <j,i> ,容量都为 1 1 1
  • 这样的含义是,如果保留了边 &lt; S , i &gt; &lt;S,i&gt; <S,i> 和 边 &lt; j , T &gt; &lt;j,T&gt; <j,T> ,那么还需要额外割掉边 &lt; i , j &gt; &lt;i,j&gt; <i,j> ,否则存在一条 S S S T T T 的路径 S − &gt; i − &gt; j − &gt; T S-&gt;i-&gt;j-&gt;T S>i>j>T
  • &lt; j , i &gt; &lt;j,i&gt; <j,i> 同理
  • 实际上容量为 0 0 0 的边没必要建,在上面只是为了便于理解而建出来
  • 求一遍最小割即为答案
  • 此外,如果二元组 ( i , j ) (i,j) (i,j) 是限制 x i x_i xi 必须等于 x j x_j xj ,则只需要把 &lt; i , j &gt; &lt;i,j&gt; <i,j> &lt; j , i &gt; &lt;j,i&gt; <j,i> 的容量设为 ∞ \infty (表示该边割不掉)即可

代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
#define NF(u) for (int &e = cur[u], v = go[e]; e; e = nxt[e], v = go[e])

inline int read()
{
   
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

template <class T>
inline T Min(const T &a, const T &b) {
   return a < b ? a : b;}

const int N = 610, M = 6e5 + 5, INF = 0x3f3f3f3f;

int n, m, ecnt = 1, nxt[M], adj[N], go[M], cap[M], S, T, lev[N],
len, que[N], cur[N], ans;

void add_edge(int u, int v, int w)
{
   
	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; cap[ecnt] = w;
	nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; cap[ecnt] = 0;
}

bool bfs()
{
   
	int i;
	For (i, S, T) cur[i] = adj[i], lev[i] = -1;
	lev[que[len = 1] = S] = 0;
	For (i, 1, len)
	{
   
		int u = que[i];
		Edge(u) if (cap[e] && lev[v] == -1)
		{
   
			lev[que[++len] = v] = lev[u] + 1;
			if (v == T) return 1;
		}
	}
	return 0;
}

int dinic(int u, int flow)
{
   
	if (u == T) return flow;
	int res = 0, delta;
	NF(u) if (cap[e] && lev[u] < lev[v])
	{
   
		delta = dinic(v, Min(cap[e], flow - res));
		if (delta)
		{
   
			cap[e] -= delta; cap[e ^ 1] += delta;
			res += delta; if (res == flow) break;
		}
	}
	if (res != flow) lev[u] = -1;
	return res;
}

int main()
{
   
	int i, x, y;
	n = read(); m = read();
	S = 1; T = n + 2;
	For (i, 1, n)
	{
   
		x = read();
		if (x) add_edge(1 + i, T, 1);
		else add_edge(S, 1 + i, 1);
	}
	while (m--) x = read(), y = read(),
		add_edge(x + 1, y + 1, 1), add_edge(y + 1, x + 1, 1);
	while (bfs()) ans += dinic(S, INF);
	std::cout << ans << std::endl;
	return 0;
}

(2)[BZOJ2127]happiness

题意

  • n × m n\times m n×m 的变量矩阵 x i , j x_{i,j} xi,j
  • 对于任意的 1 ≤ i ≤ n , 1 ≤ j ≤ m 1\le i\le n,1\le j\le m 1in,1jm
  • 如果 x i , j = 0 x_{i,j}=0 xi,j=0 则有 a i , j a_{i,j} ai,j 的收益
  • 如果 x i , j = 1 x_{i,j}=1 xi,j=1 则有 b i , j b_{i,j} bi,j 的收益
  • 对于每个相邻(有且仅有一条公共边)的格子 ( i , j ) (i,j) (i,j) ( u , v ) (u,v) (u,v)
  • 如果 x i , j = x u , v = 0 x_{i,j}=x_{u,v}=0 xi,j=xu,v=0 则有 c i , j , u , v c_{i,j,u,v} ci,j,u,v 的收益
  • 如果 x i , j = x u , v = 1 x_{i,j}=x_{u,v}=1 xi,j=xu,v=1 则有 d i , j , u , v d_{i,j,u,v} di,j,u,v 的收益
  • n , m ≤ 100 n,m\le 100 n,m100 0 ≤ a i , j , b i , j , c i , j , u , v , d i , j , u , v ≤ 5000 0\le a_{i,j},b_{i,j},c_{i,j,u,v},d_{i,j,u,v}\le 5000 0ai,j,bi,j,ci,j,u,v,di,j,u,v5000

做法

  • 转化问题
  • 如果 x i , j = 0 x_{i,j}=0 xi,j=0 则有 b i , j b_{i,j} bi,j 的代价
  • 如果 x i , j = 1 x_{i,j}=1 xi,j=1 则有 a i , j a_{i,j} ai,j 的代价
  • 对于每个相邻(有且仅有一条公共边)的格子 ( i , j ) (i,j) (i,j) ( u , v ) (u,v) (u,v)
  • 如果 ( i , j ) = 0 (i,j)=0 (i,j)=0 ( u , v ) = 0 (u,v)=0 (u,v)=0 则有 d i , j , u , v d_{i,j,u,v} di,j,u,v 的代价
  • 如果 ( i , j ) = 1 (i,j)=1 (i,j)=1 ( u , v ) = 1 (u,v)=1 (u,v)=1 则有 c i , j , u , v c_{i,j,u,v} ci,j,u,v 的代价
  • ∑ a + ∑ b + ∑ c + ∑ d − 最 小 代 价 和 \sum a+\sum b+\sum c+\sum d-最小代价和 a+b+c+d
  • 发现与上一题不同的地方就在于 c 和 d 不相等
  • 所以我们考虑设
  • p i , j = a i , j + ∑ i ≤ u , j ≤ v c i , j , u , v p_{i,j}=a_{i,j}+\sum_{i\le u,j\le v}c_{i,j,u,v} pi,j=ai,j+iu,jvci,j,u,v
  • q i , j = b i , j + ∑ i ≤ u , j ≤ v d i , j , u , v q_{i,j}=b_{i,j}+\sum_{i\le u,j\le v}d_{i,j,u,v} qi,j=bi,j+iu,jvdi,j,u,v
  • S S S 向点 ( i , j ) (i,j) (i,j) 连容量为 p i , j p_{i,j} pi,j 的边
  • ( i , j ) (i,j) (i,j) T T T 连容量为 q i , j q_{i,j} qi,j 的边
  • 对于每个 ( i , j , u , v ) (i,j,u,v) (i,j,u,v) i ≤ u , j ≤ v i\le u,j\le v iu,jv
  • 由点 ( i , j ) (i,j) (i,j) 向点 ( u , v ) (u,v) (u,v) 连容量为 c i , j , u , v c_{i,j,u,v} ci,j,u,v 的边
  • 由点 ( u , v ) (u,v) (u,v) 向点 ( i , j ) (i,j) (i,j
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值