线性规划-对偶型-单纯型法-学习笔记
i207M
2019-02-20 16:42:13
## 线性规划标准型与对偶型
先说对偶型:
![](https://cdn.luogu.com.cn/upload/pic/25547.png)
![](https://cdn.luogu.com.cn/upload/pic/25548.png)
![](https://cdn.luogu.com.cn/upload/pic/25546.png)
有什么用呢?比方说:
在 ? 天内,第 ? 天每陪妹子 1 小时就要付出 ?_? 的代价(陪了妹子就不能刷题了);每连续的 7 天里至少陪妹子 7 个小时(不然妹子就跑了);求至少需要付出多少代价,?≤10000;
![](https://cdn.luogu.com.cn/upload/pic/25549.png)
![](https://cdn.luogu.com.cn/upload/pic/25550.png)
但是,直接上差分约束,并没有什么好方法,因为ci不同,差分约束无法最大化答案;
**对偶!**
![](https://cdn.luogu.com.cn/upload/pic/25551.png)
目标函数非常简单,差分约束条件可以写出来,那就走起;
![](https://cdn.luogu.com.cn/upload/pic/25552.png)
# 看到的优秀讲解,茅塞顿开
对偶有什么用呢?变量多限制少不好弄,对偶后变量少限制多,但是可以结合实际取点一些限制!
![捕获.png](https://i.loli.net/2019/06/12/5d0038f85668f75552.png)
[link](https://www.zhihu.com/question/26658861)
**给原材料定价,使得不低于售价**
对于[Farmville](https://vjudge.net/problem/TopCoder-13952)这道题(原题目为最小化,对偶为最大化),我们有若干$x_i\ge x_j+a_i$,花费b_i可以使$a_i-1$的边,我们先用直觉建图,然后$swap(flow,cost)$即可。
原题目为最大化,对偶为最小话,那就是边为$x_i\le x_j+a_i$,花费$b_i$可以使$a_i+1$。
**感觉和差分约束有千丝万缕的关系!**
--------------
## 单纯型法
解释不太清楚...
一种时间复杂度指数级的算法,但是因为有跑不满的信仰,所以在不特殊构造的情况下能跑几百的点。
[论文](https://wenku.baidu.com/view/ce5784754a7302768f99391d)
[zzq的Blog](http://www.cnblogs.com/zzqsblog/p/5457091.html)
简单的说,我们先要把一般的线性规划问题转化为标准型
![](https://cdn.luogu.com.cn/upload/pic/52330.png)
![捕获.PNG](https://i.loli.net/2019/02/20/5c6d3c7113afc.png)
几何意义:首先,最优解一定是在可行区域的边界取到的。单纯形法的过程就相当于沿着边界做爬山算法。
**要求**:$b_i\ge 0$(下面会说不满足了怎么办)
终止条件:
1. 若存在$c_i\ge 0$,则未终止,找到了可行解。
2. 若$c_i\le 0$,则找到了最优解。若存在$c_i=0$,则最优解不唯一。
过程:
1. 找到入基变量e。要求:第一个$i$,使得$c_i>0$
2. 找到出基变量l。要求:最小化$a[l][0]/a[l][e] > 0$
3. pivot,继续进行。
pivot的过程:看代码吧。
--------------
解决$b_i<0$
方法一:
![捕获.PNG](https://i.loli.net/2019/02/20/5c6d3e6697e66.png)
方法二:
利用pivot操作,
在bi为负的时候,把所有基变量设为0不是一组合法的初始解,所以选择一个bi为负的基变量x[i+n],然后在该约束右边找一个系数为正(即原系数为负)的非基变量进行转轴操作,如果没有系数为正显然就无解了。
-----------------
小Trick:
对于全幺模矩阵,直接用int存就好了。
全幺模矩阵:所有子矩阵(可以不连续)都是幺模矩阵。
幺模矩阵:行列式为1或-1.
判定全幺模矩阵的一些充要条件:
1. 能表示成网络流(能网络流我要你干甚)
2. 有一种列的排列能使得每行的1是连续的
```cpp
#define N 23
#define eps 1e-8
int n,m;
int id[N*2];
double a[N][N],ans[N];
void pivot(int l,int e)
{
swap(id[n+l],id[e]);
double t=a[l][e]; a[l][e]=1;
for(ri i=0; i<=n; ++i) a[l][i]/=t;
for(ri i=0; i<=m; ++i)
{
if(i==l) continue;
t=a[i][e]; a[i][e]=0;
for(ri j=0; j<=n; ++j)
a[i][j]-=t*a[l][j];
}
}
signed main()
{
#ifdef M207
freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
#endif
int typ; in(n,m,typ);
for(ri i=1; i<=n; ++i) a[0][i]=rd(),id[i]=i;
for(ri i=1; i<=m; ++i)
{
for(ri j=1; j<=n; ++j) a[i][j]=rd();
a[i][0]=rd();
}
while(1)
{
int l=0,e=0;
for(ri i=1; i<=m; ++i)
if(a[i][0]<-eps)
{
l=i;
break;
}
if(!l) break;
double t=-eps;
for(ri i=1; i<=n; ++i)
if(a[l][i]<t)
{
t=a[l][i];
e=i;
}
if(!e) {puts("Infeasible"); return 0;}
pivot(l,e);
}
while(1)
{
int l=0,e=0;
for(ri i=1; i<=n; ++i)
if(a[0][i]>eps)
{
e=i;
break;
}
if(!e) break;
double t=1e100;
for(ri i=1; i<=m; ++i)
if(a[i][e]>eps&&a[i][0]/a[i][e]<t)
{
t=a[i][0]/a[i][e];
l=i;
}
if(!l) {puts("Unbounded"); return 0;}
pivot(l,e);
}
printf("%.10f\n",-a[0][0]);
if(typ)
{
for(ri i=1; i<=m; ++i) ans[id[n+i]]=a[i][0];
for(ri i=1; i<=n; ++i) printf("%.10f ",ans[i]);
}
return 0;
}
```
-------
## 例题
BZOJ 3118
~~感觉像网络流,但其实不是~~
[膜Claris题解](https://www.cnblogs.com/clrs97/p/4403183.html)
设$x[i]$为第i条边的变化量,树边减少,非树边增加
对于每条非树边i(u,v),在树上u到v路径上的所有边j都都要满足$W_i+X_i\geq W_j-X_j$
移项后得到$X_i+X_j\geq W_j-W_i$,对于对偶问题用单纯型法。