线性规划对偶原理学习笔记
单纯形算法先咕咕
对偶原理:
- 若线性规划\(A\): (\(x\)变量,\(c\)常数)
- 最大化 \(\sum_ic_i*x_i\)
- 约束条件:\(a_{i,j} * x_i \leq b_i\)
- \(x_i \geq 0\)
- 线性规划\(B\):
- 最小化\(\sum_jb_j*y_j\)
- 约束条件:\(a_{i,j}*y_j \geq c_j\)
- \(y_j \geq 0\)
- 则称\(A\)与\(B\)是对偶问题
定理:对偶问题的最优解值相同
证明:不会
写成矩阵的形式:
- \(maximize\ C^T * X\)
- \(s.t.1. A * X \leq B\)\(\ 2. \ X\geq 0\)
对偶:
- \(minimize\ B^T * Y\)
- \(s.t.1.A^T * Y \geq C\ 2.\ Y \geq 0\)
\(example:\)二分图最大权匹配等于最小顶标和
- 定义:在二分图中,每个点都有一个顶标,要求每条边的两个端点的顶标之和\(\geq\)边权,并最小化全局顶标和,称为最小顶标和问题.
- 特别的,若边权都为\(1\),则有弱定理:二分图最大匹配等于最小点覆盖
\(proof:\)
\[\begin{aligned}
&考虑将最大权匹配写成线性规划的形式\\
&maximize: \sum_{(u,v)\in E}c_{u,v}*d_{u,v}\\
&s.t. \sum_{u \in left}d_{u,v} \leq 1\\
&s.t. \sum_{v \in right}d_{u,v} \leq 1\\
&st. d_{u,v} \geq 0\\
&考虑其对偶形式\\
&minimize: \sum_{u \in left}p_u + \sum_{v \in right}p_v\\
&s.t.p_u + p_v \geq c_{u,v}\\
&s.t.p_u \geq 0,p_v \geq 0\\
\end{aligned}\]
考虑对偶形式的组合意义即可完成证明
\(tips:\)可以形象的认为原来的矩阵倒过来看.如上面原来是一个V * E的矩阵,V个点,每个点E个约束,倒过来看就是E * V的矩阵.其实就对应着矩阵形式的转置
-
例题:ZJOI2020序列
- 题意简述:有一个长度为\(n\)的非负整数序列\(a\),你可以执行以下三种操作使得该序列变为全\(0\)
- 选一个区间\([l,r]\)将其中所有数\(-1\)
- 选一个区间\([l,r]\)将其中所有下标为偶数的数\(-1\)
- 选一个区间\([l,r]\)将其中所有下标为奇数的数\(-1\)
- solution:
- 考虑列出线性规划
- 设\(d_{l,r,0/1/2}\)表示在\([l,r]\)这个区间执行以上三种操作的次数
\[\begin{aligned}
&min \sum_{l\leq r}d_{l,r,0/1/2}\\
&s.t.\sum{d_{l,r,0}} + \sum{d_{l,r,1}} == x_i(i是偶数,且i\in[l,r])\\
&s.t.\sum{d_{l,r,0}} + \sum{d_{l,r,2}} == x_i(i是奇数,且i\in[l,r])\\
\end{aligned}\]
- 将等号拆成两个不等号,并将大于等于号取反变成小于等于号.对偶,则有
\[\begin{aligned}
&max \sum_{j = 1}^n(s_j-t_j)*a_j \\
&\sum_{i \in [l,r]}(s_i - t_i) \leq 1\\
&\sum_{i \in [l,r],i是奇数}(s_i - t_i) \leq 1\\
&\sum_{i \in [l,r],i是偶数}(s_i - t_i) \leq 1\\
\end{aligned}\]
不妨令\(b_i = s_i - t_i\).注意此时\(b_i\)不必大于等于\(0\)
- 我们考虑设\(dp_{i,x,y,z}\)表示当前\(dp\)到\(i\),前三个约束的最大\(b_i\)和分别为\(x,y,z\)的最大值
- 我们考虑\(x,y,z\)的有效取值一定大于等于\(0\),因为如果前面的小于\(0\),我们可以从这个位置开始,那就是\(== 0\)了
- 所以\(0 \leq x,y,z \leq 1\)
- 考虑可以证明这是个整数规划(暂时没想到什么好的解释).所以直接\(dp\)即可
- 那么\(b_i\)的取值也只可能是\(-1,0,1\)
于是我们成功用对偶原理把\(ZJOI\)变成普及\(dp\)
/*[ZJOI2020] 序列*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int read(){
char c = getchar();
int x = 0;
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar();
return x;
}
const int _ = 1e5 + 7;
ll dp[_][2][2][2];
int a[_];
void solve(){
int n = read();
for(int i = 1; i <= n; ++i) a[i] = read();
memset(dp,0xcf,sizeof(dp));
dp[0][0][0][0] = 0;
for(int i = 0; i < n; ++i){
for(int x = 0; x <= 1; ++x){
for(int y = 0; y <= 1; ++y){
for(int z = 0; z <= 1; ++z){
for(int s = -1; s <= 1; ++s){
if(i & 1){
int nx = max(x + s,0);
int ny = max(y + s,0);
if(nx > 1 || ny > 1) continue;
int nz = z;
dp[i+1][nx][ny][nz] = max(dp[i+1][nx][ny][nz],dp[i][x][y][z] + s * a[i+1]);
}
else{
int nx = max(x + s,0);
int ny = y;
int nz = max(z + s,0);
if(nx > 1 || nz > 1) continue;
dp[i+1][nx][ny][nz] = max(dp[i+1][nx][ny][nz],dp[i][x][y][z] + s * a[i+1]);
}
}
}
}
}
}
ll ans = 0;
for(int x = 0; x <= 1; ++x)
for(int y = 0; y <= 1; ++y)
for(int z = 0; z <= 1; ++z) ans = max(ans,dp[n][x][y][z]);
printf("%lld\n",ans);
}
int main(){
int T = read();
while(T--) solve();
return 0;
}