原问题:
min { 7 x 1 + x 2 + 5 x 3 } s . t . { x 1 − x 2 + 3 x 3 ≥ 10 5 x 1 + 2 x 2 − x 3 ≥ 6 x i ≥ 0 \min\{7x_1+x_2+5x_3\} \\ s.t.\begin{cases} x_1-x_2+3x_3\ge 10\\ 5x_1+2x_2-x_3\ge 6\\ x_i\ge 0\end{cases} min{7x1+x2+5x3}s.t.⎩⎪⎨⎪⎧x1−x2+3x3≥105x1+2x2−x3≥6xi≥0我们发现限制Ⅰ,Ⅱ 的每个 x i x_i xi 系数均 ≤ \le ≤ 原问题中的系数,所以我们可以初步得到原问题的取值范围限制 min { 7 x 1 + x 2 + 5 x 3 } ≥ 10 \min\{7x_1+x_2+5x_3\}\ge 10 min{7x1+x2+5x3}≥10。
进一步我们发现限制Ⅰ,Ⅱ 相加后的 x i x_i xi 系数仍然 ≤ \le ≤ 原问题中的系数,于是乎我们“加紧”了原问题的取值范围 min { 7 x 1 + x 2 + 5 x 3 } ≥ 16 \min\{7x_1+x_2+5x_3\}\ge 16 min{7x1+x2+5x3}≥16。
我们是否能将原问题的范围“加到最紧”,即最大化最小值。
这显然是可以的。我们不妨给两个限制各一个系数 y i y_i yi。
则我们可以写出一个类似原问题形式的线性规划问题,称之为对偶问题。
对偶问题:
max { 10 y 1 + 6 y 2 } s . t . { y 1 + 5 y 2 ≤ 7 − y 1 + 2 y 2 ≤ 1 3 y 1 − y 2 ≤ 5 y i ≥ 0 \max\{10y_1+6y_2\} \\ s.t.\begin{cases}y_1+5y_2\le 7\\ -y_1+2y_2\le 1\\ 3y_1-y_2\le 5\\ y_i\ge 0\end{cases} max{10y1+6y2}s.t.⎩⎪⎪⎪⎨⎪⎪⎪⎧y1+5y2≤7−y1+2y2≤13y1−y2≤5yi≥0一个最大化问题可以对偶成最小化问题,一个最小化问题可以对偶成最大化问题。
给出例子中的矩阵形式:
原问题:
min [ 7 1 5 ] ⋅ [ x 1 x 2 x 3 ] s . t . { [ 1 − 1 3 5 2 − 1 ] ⋅ [ x 1 x 2 x 3 ] ≥ [ 10 6 ] ∀ i x i ≥ 0 \min\begin{bmatrix}7\quad1\quad5\end{bmatrix}·\begin{bmatrix}x_1\\x_2\\x_3\end{bmatrix} \\ s.t.\begin{cases}\begin{bmatrix}1&-1&3\\5& 2&-1\end{bmatrix}·\begin{bmatrix}x_1\\x_2\\x_3\end{bmatrix}\ge \begin{bmatrix}10\\6\end{bmatrix}\\\forall_i\ x_i\ge 0\end{cases} min[715]⋅⎣⎡x1x2x3⎦⎤s.t.⎩⎪⎪⎨⎪⎪⎧[15−123−1]⋅⎣⎡x1x2x3⎦⎤≥[106]∀i xi≥0对偶问题:
max [ 10 6 ] ⋅ [ y 1 y 2 y 3 ] s . t . { [ 1 5 − 1 2 3 − 1 ] ⋅ [ y 1 y 2 ] ≤ [ 7 1 5 ] ∀ i y i ≥ 0 \max\begin{bmatrix}10&6\end{bmatrix}·\begin{bmatrix}y_1\\y_2\\y_3\end{bmatrix} \\ s.t.\begin{cases}\begin{bmatrix}1&5\\-1&2\\3&-1\end{bmatrix}·\begin{bmatrix}y_1\\y_2\end{bmatrix}\le \begin{bmatrix}7\\1\\5\end{bmatrix}\\\forall_i\ y_i\ge 0\end{cases} max[106]⋅⎣⎡y1y2y3⎦⎤s.t.⎩⎪⎪⎨⎪⎪⎧⎣⎡1−1352−1⎦⎤⋅[y1y2]≤⎣⎡715⎦⎤∀i yi≥0
形式化地给出原问题和对偶问题之间的转化:(注意下标)
原问题:
min ∑ j = 1 n c j x j s . t . { ∀ 1 ≤ i ≤ m ∑ a i , j ⋅ x j ≥ b i ∀ i x i ≥ 0 \min \sum_{j=1}^n c_jx_j \\ s.t.\begin{cases} \forall_{1\le i\le m}\ \sum a_{i,j}·x_j\ge b_i\\ \forall_{i}\ x_i\ge 0 \end{cases} minj=1∑ncjxjs.t.{∀1≤i≤m ∑ai,j⋅xj≥bi∀i xi≥0对偶问题:
max ∑ i = 1 m b i y i s . t . { ∀ 1 ≤ j ≤ n ∑ a i , j ⋅ y i ≤ c j ∀ i y i ≥ 0 \max \sum_{i=1}^m b_iy_i \\ s.t.\begin{cases} \forall_{1\le j\le n} \sum a_{i,j}·y_i\le c_j\\ \forall_i\ y_i\ge 0 \end{cases} maxi=1∑mbiyis.t.{∀1≤j≤n∑ai,j⋅yi≤cj∀i yi≥0考虑用矩阵形式来表示,之所以有矩阵形式,好像是因为有些题目矩阵好推一点。
原问题:
min { c T x } s . t . { A x ≥ b x ≥ 0 \min\{c^Tx\} \\ s.t.\begin{cases}Ax\ge b\\ x\ge 0\end{cases} min{cTx}s.t.{Ax≥bx≥0对偶问题:
max { b T y } s . t . { A T y ≤ c y ≥ 0 \max\{b^Ty\}\\ s.t.\begin{cases} A^Ty\le c\\ y\ge 0 \end{cases} max{bTy}s.t.{ATy≤cy≥0
在一张 n n n 个点 m m m 条边的网络图中,我们这里只研究循环流,以最大费用循环流为例。
声明:将一条 u → v u\rightarrow v u→v 的有向边表示成 ( u , v ) (u,v) (u,v),将一个 n n n 行 m m m 列的矩阵 A A A 表示成 [ n , m ] A [n,m]A [n,m]A,矩阵间的大小关系是按位均满足。
原问题:
max ∑ e c o s t e ⋅ f l o w e ⇔ max [ 1 , m ] c o s t T ⋅ [ m , 1 ] f l o w s . t . { ∀ f l o w u , v ≥ 0 ∀ f l o w u , v ≤ c a p u , v ∀ u ∑ f l o w v , u − ∑ f l o w u , v = b u \max \sum_{e}cost_e·flow_e\Leftrightarrow \max\ [1,m]cost^T·[m,1]flow \\ s.t.\begin{cases} \forall\ flow_{u,v}\ge 0\\ \forall\ flow_{u,v}\le cap_{u,v}\\ \forall_u\ \sum flow_{v,u}-\sum flow_{u,v}=b_u\\ \end{cases} maxe∑coste⋅flowe⇔max [1,m]costT⋅[m,1]flows.t.⎩⎪⎨⎪⎧∀ flowu,v≥0∀ flowu,v≤capu,v∀u ∑flowv,u−∑flowu,v=bu b u : b_u: bu: 流入 − - − 流出,但要求 b u b_u bu 一定是已知的常量。
实际上我们可以写成 ∀ u ∑ f l o w v , u − ∑ f l o w u , v ≤ b u \forall_u\ \sum flow_{v,u}-\sum flow_{u,v}\le b_u ∀u ∑flowv,u−∑flowu,v≤bu。
因为在一个图中,要求所有点入度 ≤ \le ≤ 出度,最后成立情况一定是每个点入度 = = = 出度。感性理解好了┏┛墓┗┓…(((m -__-)m
我们直接用矩阵形式来推。
首先第一种限制和第二种只针对一条边单独而言,可以合起来。我们配个单位矩阵即可。
[ 1 0 0 . . . 0 0 1 0 . . . 0 0 0 1 . . . 0 . . . . . . . . . . . . . . . 0 0 0 . . . 1 ] ⋅ [ f l o w 1 f l o w 2 f l o w 3 . . . f l o w m ] ≤ [ c a p 1 c a p 2 c a p 3 . . . c a p m ] \begin{bmatrix}1&0&0&...&0\\0&1&0&...&0\\0&0&1&...&0\\...&...&...&...&...\\0&0&0&...&1\end{bmatrix}·\begin{bmatrix}flow_1\\flow_2\\flow_3\\...\\flow_m\end{bmatrix}\le \begin{bmatrix}cap_1\\cap_2\\cap_3\\...\\cap_m\end{bmatrix} ⎣⎢⎢⎢⎢⎡100...0010...0001...0...............000...1⎦⎥⎥⎥⎥⎤⋅⎣⎢⎢⎢⎢⎡flow1flow2flow3...flowm⎦⎥⎥⎥⎥⎤≤⎣⎢⎢⎢⎢⎡cap1cap2cap3...capm⎦⎥⎥⎥⎥⎤第三种限制涉及到求和,比较不直观,但还是可以写的。
定义一个 n × m n\times m n×m 的矩阵 Z Z Z,规则: Z i , j = { − 1 j u = i 1 j v = i 0 j u ≠ i ∧ j v ≠ i Z_{i,j}=\begin{cases}-1\ \quad\ j_u=i\\1\quad\quad\ j_v=i\\0\quad\quad\ j_u\neq i\wedge j_v\ne i\end{cases} Zi,j=⎩⎪⎨⎪⎧−1 ju=i1 jv=i0 ju=i∧jv=i
中国话说即为: i i i 是点, j j j 是边。如果第 j j j 条边中 i i i 充当出点 u u u 则为 − 1 -1 −1;充当入点 v v v 则为 1 1 1;否则为 0 0 0。
则第三种限制就可以看作是 [ n , m ] Z ⋅ [ m , 1 ] f l o w ≤ [ n , 1 ] b [n,m]Z·[m,1]flow\le [n,1]b [n,m]Z⋅[m,1]flow≤[n,1]b。
整体整合表示即:
[ m , m ] I [ n , m ] Z ⋅ [ m , 1 ] f l o w ≤ [ m , 1 ] c a p [ n , 1 ] b \frac{[m,m]I}{[n,m]Z}·[m,1]flow\le \frac{[m,1]cap}{[n,1]b} [n,m]Z[m,m]I⋅[m,1]flow≤[n,1]b[m,1]cap
由此我们可以写出对偶问题:( [ m , m ] I [ n , m ] Z \frac{[m,m]I}{[n,m]Z} [n,m]Z[m,m]I 即为形式化中的 A A A)
对偶问题:
min [ 1 , m + n ] ( c a p T ∣ b T ) ⋅ [ m + n , 1 ] Y s . t . { [ m , m + n ] ( [ m , m ] I T ∣ [ m , n ] Z T ) ⋅ [ m + n , 1 ] Y ≥ [ m , 1 ] c o s t ∀ i Y i ≥ 0 \min [1,m+n]\Big(cap^T\Big| b^T\Big)·[m+n,1]Y \\ s.t.\begin{cases}[m,m+n]\Big([m,m]I^T\Big|[m,n]Z^T\Big)·[m+n,1]Y\ge [m,1]cost\\ \forall_i\ Y_i\ge 0\end{cases} min[1,m+n](capT∣∣∣bT)⋅[m+n,1]Ys.t.{[m,m+n]([m,m]IT∣∣∣[m,n]ZT)⋅[m+n,1]Y≥[m,1]cost∀i Yi≥0矩阵的点积是对位相乘后相加,所以我们可以把 [ m + n , 1 ] Y [m+n,1]Y [m+n,1]Y 拆成 [ m , 1 ] y ∣ [ n , 1 ] φ [m,1]y\Big|[n,1]\varphi [m,1]y∣∣∣[n,1]φ。
min c a p T ⋅ y + b T ⋅ φ s . t . { y i ≥ 0 φ i ≥ 0 I T ⋅ y + Z T ⋅ φ ≥ c o s t \min\ cap^T·y+b^T·\varphi\\s.t.\begin{cases} y_i\ge 0\\ \varphi_i\ge 0\\ I^T·y+Z^T·\varphi\ge cost\end{cases} min capT⋅y+bT⋅φs.t.⎩⎪⎨⎪⎧yi≥0φi≥0IT⋅y+ZT⋅φ≥cost非常巧妙的是,注意 Z Z Z 矩阵转置后 Z T Z^T ZT 具有的特殊性质。
我们重申一下 Z T Z^T ZT 的含义: Z i , j T = { − 1 i u = j 1 i v = j 0 i u ≠ j ∧ i v ≠ j Z^T_{i,j}=\begin{cases}-1&i_u=j\\1&i_v=j\\0&i_u\ne j \wedge i_v\ne j\end{cases} Zi,jT=⎩⎪⎨⎪⎧−110iu=jiv=jiu=j∧iv=j,也就是说此时的 i i i 是边, j j j 是点。
考虑任意一条边 i i i,它只会关联到两个点,所以矩阵的第 i i i 行只有两列非零,且恰好是 ± 1 ±1 ±1 各一个。
那么第三条矩阵点积的约束我们可以重写成 y i + φ v − φ u − c o s t i ≥ 0 y_i+\varphi_v-\varphi_u-cost_i\ge 0 yi+φv−φu−costi≥0。
实际上写成 φ u − φ v \varphi_u-\varphi_v φu−φv 也是可以的,因为在费用流中将所有边取反(包括和源汇点之间的边)是没有任何影响的。
整合第一条限制得到: y i ≥ max ( 0 , φ u − φ v + c o s t i ) y_i\ge \max(0,\varphi_u-\varphi_v+cost_i) yi≥max(0,φu−φv+costi)。
所以我们可以将对偶问题再换个形式:
min ∑ c a p i ∗ max ( 0 , φ u − φ v + c o s t i ) + ∑ b i ⋅ φ i \min\sum cap_i*\max(0,\varphi_u-\varphi_v+cost_i)+\sum b_i·\varphi_i min∑capi∗max(0,φu−φv+costi)+∑bi⋅φi这种形式下的建图方式: φ u → φ v \varphi_u\rightarrow \varphi_v φu→φv 之间边花费为 c o s t i cost_i costi,容量 c a p i cap_i capi,根据 b u b_u bu 的正负(多流出还是多流入)将 φ u \varphi_u φu 点和新建源汇对应连边,容量即为多/少的这部分 b u b_u bu,无花费。
通常情况下我们是拿到类似上述对偶问题形式的题目,然后我们就可以转化成费用流问题,就可做了。
例题. ZJOI2013防守战线
由于是区间,我们完全可以写成前缀和差分形式,记 s i = ∑ j = 1 i x j s_i=\sum_{j=1}^ix_j si=∑j=1ixj。
则:
min ∑ ( s i − s i − 1 ) c i \min \sum (s_i-s_{i-1})c_i min∑(si−si−1)ci限制可以写成 :
{ ∀ i s r i − s l i − 1 − d i ≥ 0 ∀ i s i − s i − 1 ≥ 0 ∀ i s i ≥ 0 \begin{cases}\forall_i\ s_{r_i}-s_{l_i-1}-d_i\ge 0\\ \forall_i\ s_i-s_{i-1}\ge 0\\ \forall_i\ s_i\ge 0\end{cases} ⎩⎪⎨⎪⎧∀i sri−sli−1−di≥0∀i si−si−1≥0∀i si≥0 ∑ ( s i − s i − 1 ) c i \sum (s_i-s_{i-1})c_i ∑(si−si−1)ci 我们换一种形式。发现 s i s_i si 在 i i i 时前面是正贡献 c i ∗ s i c_i*s_i ci∗si,在 i + 1 i+1 i+1 时前面是负贡献 − c i + 1 ∗ s i -c_{i+1}*s_i −ci+1∗si。
所以可以写成 ∑ s i ( c i − c i + 1 ) \sum s_i(c_i-c_{i+1}) ∑si(ci−ci+1)。
还有一种巧妙的限制转化是,将约束被打坏给予一定惩罚。
即如果破坏某个约束,我们就让其获得无穷大的惩罚代价,那么在跑网络流的时候就会尽量避免获得代价,侧面完成了约束。
所以我们可以将答案形式改写成:
min ∑ s i ( c i − c i + 1 ) + ∑ ∞ ∗ max ( 0 , s i − 1 − s i ) + ∑ ∞ ∗ max ( 0 , s l − 1 − s r + d i ) \min \sum s_i(c_i-c_{i+1})+\sum\infty*\max(0,s_{i-1}-s_i)+\sum\infty*\max(0,s_{l-1}-s_r+d_i) min∑si(ci−ci+1)+∑∞∗max(0,si−1−si)+∑∞∗max(0,sl−1−sr+di)你发现完全可以和费用流对偶化简出来的式子一一对应。
∞ \infty ∞ 就是容量, s i − 1 − s i s_{i-1}-s_i si−1−si 之间边花费应为 0 0 0, s l − 1 − s r s_{l-1}-s_r sl−1−sr 边花费应为 d i d_i di。然后根据 c i − c i + 1 c_i-c_{i+1} ci−ci+1 的正负看是与源点还是汇点连边。(多流入的流量看作是需要给汇点的,多流出的流量看作是源点给的,这样每个点都平衡了,不然我真的无法理解这个推出来对应的建图方式了)
Upd:小同志灵光乍现明白了和源汇点之间建边和含义。
我们的网络流一定是流量平衡的,即流入等于流出。
然而我们推推导中一般化了每个点的流入可以最多比流出多 b u b_u bu。
网络路中源点向某个点连边后,这个点一定会把流量流出去。所以与源点的边相当于是增加了这个点的流出流量。自然与汇点的边就是增加流入流量。
因为前面一个点的流入和流出根本没有算和源汇的边,源汇点是后面才新建立的。
这样建图就能正确对应了。
跑最大费用流即可。
其实一般只要化出诸如 ∑ c a p i , j ∗ max ( 0 , φ i − φ j + c o s t i , j ) \sum cap_{i,j}*\max(0,\varphi_i-\varphi_j+cost_{i,j}) ∑capi,j∗max(0,φi−φj+costi,j) 形式就是指导我们如何建边。
而 ∑ b i φ i \sum b_i\varphi_i ∑biφi 是指导我们如何分配正负点。
这种“势能”的 φ \varphi φ 代表的就是图中的点。
要注意的是,这种下标 − 1 , + 1 -1,+1 −1,+1 需要看是否要用到 0 , n + 1 0,n+1 0,n+1 这两个点。
你建完了跑不出答案就考虑加点、改变边的方向、改变和源汇点的连边。只要大体是对的,总有一种是对的
单纯最大费用流只能得70,写 zkw 费用流就可以了。这里主要是分析建图。
#include
using namespace std;
#define int long long
#define inf 0x3f3f3f3f
#define maxn 2000
#define maxm 50000
int n, m, s, t, cnt = -1;
int head[maxn], dis[maxn], lst[maxn], vis[maxn], c[maxn];
struct node { int to, nxt, flow, cost; }E[maxm];
queue < int > q;
void addedge( int u, int v, int w, int c ) {
E[++ cnt] = { v, head[u], w, c }, head[u] = cnt;
E[++ cnt] = { u, head[v], 0,-c }, head[v] = cnt;
}
bool SPFA() {
memset( lst, -1, sizeof( lst ) );
memset( dis, -0x3f, sizeof( dis ) );
dis[s] = 0, q.push( s );
while( ! q.empty() ) {
int u = q.front(); q.pop(); vis[u] = 0;
for( int i = head[u];~ i;i = E[i].nxt ) {
int v = E[i].to;
if( dis[v] < dis[u] + E[i].cost and E[i].flow ) {
dis[v] = dis[u] + E[i].cost; lst[v] = i;
if( ! vis[v] ) vis[v] = 1, q.push( v );
}
}
}
return ~lst[t];
}
int MCMF() {
int ans = 0;
while( SPFA() ) {
int flow = inf;
for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] )
flow = min( flow, E[i].flow );
for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] ) {
E[i ^ 1].flow += flow;
E[i].flow -= flow;
ans += E[i].cost * flow;
}
}
return ans;
}
signed main() {
scanf( "%lld %lld", &n, &m );
s = n + 2, t = n + 1;
memset( head, -1, sizeof( head ) );
for( int i = 1;i <= n;i ++ )
scanf( "%lld", &c[i] );
for( int i = 0;i < n;i ++ )
addedge( i, i + 1, inf, 0 );
for( int i = 1, l, r, w;i <= m;i ++ ) {
scanf( "%lld %lld %lld", &l, &r, &w );
addedge( l - 1, r, inf, w );
}
for( int i = 0;i <= n;i ++ )
if( c[i] - c[i + 1] > 0 ) addedge( s, i, c[i] - c[i + 1], 0 );
else addedge( i, t, c[i + 1] - c[i], 0 );
/*边全反向
for( int i = 0;i < n;i ++ )
addedge( i + 1, i, inf, 0 );
for( int i = 1, l, r, w;i <= m;i ++ ) {
scanf( "%lld %lld %lld", &l, &r, &w );
addedge( r, l - 1, inf, w );
}
for( int i = 0;i <= n;i ++ )
if( c[i] - c[i + 1] > 0 ) addedge( i, t, c[i] - c[i + 1], 0 );
else addedge( s, i, c[i + 1] - c[i], 0 );
*/
printf( "%lld\n", MCMF() );
return 0;
}
欢迎指出错误,我巴不得有人说这是错的然后把对的教给我球球了