【学习笔记】线性规划与对偶问题和LP对偶费用流([ZJOI2013]防守战线题解)

线性规划与对偶问题

原问题:
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.x1x2+3x3105x1+2x2x36xi0我们发现限制Ⅰ,Ⅱ 的每个 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+5y27y1+2y213y1y25yi0一个最大化问题可以对偶成最小化问题,一个最小化问题可以对偶成最大化问题。

给出例子中的矩阵形式:

原问题:
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]x1x2x3s.t.[151231]x1x2x3[106]i xi0对偶问题:
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]y1y2y3s.t.113521[y1y2]715i yi0


形式化地给出原问题和对偶问题之间的转化:(注意下标)

原问题:
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=1ncjxjs.t.{1im ai,jxjbii xi0对偶问题:
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=1mbiyis.t.{1jnai,jyicji yi0考虑用矩阵形式来表示,之所以有矩阵形式,好像是因为有些题目矩阵好推一点。

原问题:
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.{Axbx0对偶问题:
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.{ATycy0


LP对偶费用流

在一张 n n n 个点 m m m 条边的网络图中,我们这里只研究循环流,以最大费用循环流为例。

声明:将一条 u → v u\rightarrow v uv 的有向边表示成 ( 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} maxecosteflowemax [1,m]costT[m,1]flows.t. flowu,v0 flowu,vcapu,vu flowv,uflowu,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,uflowu,vbu

因为在一个图中,要求所有点入度 ≤ \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...1flow1flow2flow3...flowmcap1cap2cap3...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=ijv=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](capTbT)[m+n,1]Ys.t.{[m,m+n]([m,m]IT[m,n]ZT)[m+n,1]Y[m,1]costi Yi0矩阵的点积是对位相乘后相加,所以我们可以把 [ 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 capTy+bTφs.t.yi0φi0ITy+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=jiv=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φucosti0

实际上写成 φ 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) yimax(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 mincapimax(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(sisi1)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 srisli1di0i sisi10i si0 ∑ ( s i − s i − 1 ) c i \sum (s_i-s_{i-1})c_i (sisi1)ci 我们换一种形式。发现 s i s_i si i i i 时前面是正贡献 c i ∗ s i c_i*s_i cisi,在 i + 1 i+1 i+1 时前面是负贡献 − c i + 1 ∗ s i -c_{i+1}*s_i ci+1si

所以可以写成 ∑ s i ( c i − c i + 1 ) \sum s_i(c_i-c_{i+1}) si(cici+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) minsi(cici+1)+max(0,si1si)+max(0,sl1sr+di)你发现完全可以和费用流对偶化简出来的式子一一对应。

∞ \infty 就是容量, s i − 1 − s i s_{i-1}-s_i si1si 之间边花费应为 0 0 0 s l − 1 − s r s_{l-1}-s_r sl1sr 边花费应为 d i d_i di。然后根据 c i − c i + 1 c_i-c_{i+1} cici+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,jmax(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;
}

欢迎指出错误,我巴不得有人说这是错的然后把对的教给我球球了

你可能感兴趣的:(学习,线性代数,线性规划,对偶问题,LP对偶费用流)