线性规划与对偶问题
原问题:
min{7x1+x2+5x3}s.t.{x1−x2+3x3≥105x1+2x2−x3≥6xi≥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我们发现限制Ⅰ,Ⅱ 的每个 xix_ixi 系数均 ≤\le≤ 原问题中的系数,所以我们可以初步得到原问题的取值范围限制 min{7x1+x2+5x3}≥10\min\{7x_1+x_2+5x_3\}\ge 10min{7x1+x2+5x3}≥10。
进一步我们发现限制Ⅰ,Ⅱ 相加后的 xix_ixi 系数仍然 ≤\le≤ 原问题中的系数,于是乎我们“加紧”了原问题的取值范围 min{7x1+x2+5x3}≥16\min\{7x_1+x_2+5x_3\}\ge 16min{7x1+x2+5x3}≥16。
我们是否能将原问题的范围“加到最紧”,即最大化最小值。
这显然是可以的。我们不妨给两个限制各一个系数 yiy_iyi。
则我们可以写出一个类似原问题形式的线性规划问题,称之为对偶问题。
对偶问题:
max{10y1+6y2}s.t.{y1+5y2≤7−y1+2y2≤13y1−y2≤5yi≥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[715]⋅[x1x2x3]s.t.{[1−1352−1]⋅[x1x2x3]≥[106]∀ixi≥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[106]⋅[y1y2y3]s.t.{[15−123−1]⋅[y1y2]≤[715]∀iyi≥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=1ncjxjs.t.{∀1≤i≤m∑ai,j⋅xj≥bi∀ixi≥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=1mbiyis.t.{∀1≤j≤n∑ai,j⋅yi≤cj∀iyi≥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{cTx}s.t.{Ax≥bx≥0\min\{c^Tx\} \\ s.t.\begin{cases}Ax\ge b\\ x\ge 0\end{cases} min{cTx}s.t.{Ax≥bx≥0对偶问题:
max{bTy}s.t.{ATy≤cy≥0\max\{b^Ty\}\\ s.t.\begin{cases} A^Ty\le c\\ y\ge 0 \end{cases} max{bTy}s.t.{ATy≤cy≥0
LP对偶费用流
在一张 nnn 个点 mmm 条边的网络图中,我们这里只研究循环流,以最大费用循环流为例。
声明:将一条 u→vu\rightarrow vu→v 的有向边表示成 (u,v)(u,v)(u,v),将一个 nnn 行 mmm 列的矩阵 AAA 表示成 [n,m]A[n,m]A[n,m]A,矩阵间的大小关系是按位均满足。
原问题:
max∑ecoste⋅flowe⇔max[1,m]costT⋅[m,1]flows.t.{∀flowu,v≥0∀flowu,v≤capu,v∀u∑flowv,u−∑flowu,v=bu\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=bubu:b_u:bu: 流入 −-− 流出,但要求 bub_ubu 一定是已知的常量。
实际上我们可以写成 ∀u∑flowv,u−∑flowu,v≤bu\forall_u\ \sum flow_{v,u}-\sum flow_{u,v}\le b_u∀u ∑flowv,u−∑flowu,v≤bu。
因为在一个图中,要求所有点入度 ≤\le≤ 出度,最后成立情况一定是每个点入度 === 出度。感性理解好了┏┛墓┗┓…(((m -__-)m
我们直接用矩阵形式来推。
首先第一种限制和第二种只针对一条边单独而言,可以合起来。我们配个单位矩阵即可。
[100...0010...0001...0...............000...1]⋅[flow1flow2flow3...flowm]≤[cap1cap2cap3...capm]\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×mn\times mn×m 的矩阵 ZZZ,规则:Zi,j={−1ju=i1jv=i0ju≠i∧jv≠iZ_{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
中国话说即为:iii 是点,jjj 是边。如果第 jjj 条边中 iii 充当出点 uuu 则为 −1-1−1;充当入点 vvv 则为 111;否则为 000。
则第三种限制就可以看作是 [n,m]Z⋅[m,1]flow≤[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]flow≤[m,1]cap[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 即为形式化中的 AAA)
对偶问题:
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∀iYi≥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]φ。
mincapT⋅y+bT⋅φs.t.{yi≥0φi≥0IT⋅y+ZT⋅φ≥cost\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非常巧妙的是,注意 ZZZ 矩阵转置后 ZTZ^TZT 具有的特殊性质。
我们重申一下 ZTZ^TZT 的含义:Zi,jT={−1iu=j1iv=j0iu≠j∧iv≠jZ^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,也就是说此时的 iii 是边,jjj 是点。
考虑任意一条边 iii,它只会关联到两个点,所以矩阵的第 iii 行只有两列非零,且恰好是 ±1±1±1 各一个。
那么第三条矩阵点积的约束我们可以重写成 yi+φv−φu−costi≥0y_i+\varphi_v-\varphi_u-cost_i\ge 0yi+φv−φu−costi≥0。
实际上写成 φu−φv\varphi_u-\varphi_vφu−φv 也是可以的,因为在费用流中将所有边取反(包括和源汇点之间的边)是没有任何影响的。
整合第一条限制得到:yi≥max(0,φu−φv+costi)y_i\ge \max(0,\varphi_u-\varphi_v+cost_i)yi≥max(0,φu−φv+costi)。
所以我们可以将对偶问题再换个形式:
min∑capi∗max(0,φu−φv+costi)+∑bi⋅φ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 之间边花费为 costicost_icosti,容量 capicap_icapi,根据 bub_ubu 的正负(多流出还是多流入)将 φu\varphi_uφu 点和新建源汇对应连边,容量即为多/少的这部分 bub_ubu,无花费。
通常情况下我们是拿到类似上述对偶问题形式的题目,然后我们就可以转化成费用流问题,就可做了。
例题. ZJOI2013防守战线
由于是区间,我们完全可以写成前缀和差分形式,记 si=∑j=1ixjs_i=\sum_{j=1}^ix_jsi=∑j=1ixj。
则:
min∑(si−si−1)ci\min \sum (s_i-s_{i-1})c_i min∑(si−si−1)ci限制可以写成 :
{∀isri−sli−1−di≥0∀isi−si−1≥0∀isi≥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∑(si−si−1)ci\sum (s_i-s_{i-1})c_i∑(si−si−1)ci 我们换一种形式。发现 sis_isi 在 iii 时前面是正贡献 ci∗sic_i*s_ici∗si,在 i+1i+1i+1 时前面是负贡献 −ci+1∗si-c_{i+1}*s_i−ci+1∗si。
所以可以写成 ∑si(ci−ci+1)\sum s_i(c_i-c_{i+1})∑si(ci−ci+1)。
还有一种巧妙的限制转化是,将约束被打坏给予一定惩罚。
即如果破坏某个约束,我们就让其获得无穷大的惩罚代价,那么在跑网络流的时候就会尽量避免获得代价,侧面完成了约束。
所以我们可以将答案形式改写成:
min∑si(ci−ci+1)+∑∞∗max(0,si−1−si)+∑∞∗max(0,sl−1−sr+di)\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∞ 就是容量,si−1−sis_{i-1}-s_isi−1−si 之间边花费应为 000,sl−1−srs_{l-1}-s_rsl−1−sr 边花费应为 did_idi。然后根据 ci−ci+1c_i-c_{i+1}ci−ci+1 的正负看是与源点还是汇点连边。(多流入的流量看作是需要给汇点的,多流出的流量看作是源点给的,这样每个点都平衡了,不然我真的无法理解这个推出来对应的建图方式了)
Upd:小同志灵光乍现明白了和源汇点之间建边和含义。
我们的网络流一定是流量平衡的,即流入等于流出。
然而我们推推导中一般化了每个点的流入可以最多比流出多 bub_ubu。
网络路中源点向某个点连边后,这个点一定会把流量流出去。所以与源点的边相当于是增加了这个点的流出流量。自然与汇点的边就是增加流入流量。
因为前面一个点的流入和流出根本没有算和源汇的边,源汇点是后面才新建立的。
这样建图就能正确对应了。
跑最大费用流即可。
其实一般只要化出诸如 ∑capi,j∗max(0,φi−φj+costi,j)\sum cap_{i,j}*\max(0,\varphi_i-\varphi_j+cost_{i,j})∑capi,j∗max(0,φi−φj+costi,j) 形式就是指导我们如何建边。
而 ∑biφi\sum b_i\varphi_i∑biφi 是指导我们如何分配正负点。
这种“势能”的 φ\varphiφ 代表的就是图中的点。
要注意的是,这种下标 −1,+1-1,+1−1,+1 需要看是否要用到 0,n+10,n+10,n+1 这两个点。
你建完了跑不出答案就考虑加点、改变边的方向、改变和源汇点的连边。只要大体是对的,总有一种是对的
单纯最大费用流只能得70,写 zkw 费用流就可以了。这里主要是分析建图。
#include <bits/stdc++.h>
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;
}
欢迎指出错误,我巴不得有人说这是错的然后把对的教给我球球了