线性规划对偶原理学习笔记

单纯形算法先咕咕

对偶原理:

  • 若线性规划\(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;
}
posted @ 2021-03-27 08:13  y_dove  阅读(639)  评论(0编辑  收藏  举报