标准型问题:
最大化 ∑ j = 1 n c j x j \sum\limits_{j=1}^{n}c_jx_j j=1∑ncjxj
满足约束 ∑ j = 1 n a i j ⋅ x j ≤ b i ( i = 1 , 2 , . . . , m ) \sum\limits_{j=1}^{n}a_{ij}\cdot x_j\le b_i\ (i=1,2,...,m) j=1∑naij⋅xj≤bi (i=1,2,...,m)
且 x j ≥ 0 ( j = 1 , 2 , 3 , . . . m ) x_j\geq 0\ (j=1,2,3,...m) xj≥0 (j=1,2,3,...m)
可以转换为松弛型问题:
最大化 ∑ j = 1 n c j x j \sum\limits_{j=1}^{n}c_jx_j j=1∑ncjxj
满足约束 x i + n = b i − ∑ j = 1 n a i j ⋅ x j ( i = 1 , 2 , . . . , m ) x_{i+n}=b_i-\sum\limits_{j=1}^{n}a_{ij}\cdot x_j\ (i=1,2,...,m) xi+n=bi−j=1∑naij⋅xj (i=1,2,...,m)
且 x j ≥ 0 ( j = 1 , 2 , 3 , . . . n + m ) x_j\geq 0\ (j=1,2,3,...n+m) xj≥0 (j=1,2,3,...n+m)
把约束中 x i + n x_{i+n} xi+n称为非基变量, x j x_j xj称为基变量
初始找出一个满足所有约束的初始解,通过这个解不断的更新,找到更优的解,直到找不到位置(类似爬山算法)。
可以发现这样一定能找到最优解,因为如果把求解的x当做n维空间的一个点,约束条件一定在n维空间中框出一个n维凸“多面体”,一个解为凸“多面体”的一个顶点。凸面体从哪一个发现看都是单峰的,用爬山的算法是肯定能找到最优解的。
例子:
最大化: S = 3 x 1 + x 2 + 2 x 3 S=3x_1+x_2+2x_3 S=3x1+x2+2x3
约束:
x 4 = 30 − x 1 − x 2 − 3 x 3 x 5 = 24 − 2 x 1 − 2 x 2 − 5 x 3 x 6 = 36 − 4 x 1 − x 2 − 2 x 3 x j ≥ 0 ( j = 1 , 2 , 3 ) \begin{aligned} x_4 &= 30 − x_1 − x_2 − 3x_3\\ x_5 &= 24 − 2x_1 − 2x_2 − 5x_3\\ x_6 &= 36 − 4x_1 − x_2 − 2x_3\\ x_j&\geq 0\ (j=1,2,3) \end{aligned} x4x5x6xj=30−x1−x2−3x3=24−2x1−2x2−5x3=36−4x1−x2−2x3≥0 (j=1,2,3)
显然可以看出初始解(0,0,0,30,24,36)
把第三个式子化为:
x 1 = 9 − 1 4 x 6 − 1 4 x 2 − 1 2 x 3 x_1=9-\frac 1 4 x_6-\frac 1 4 x_2-\frac 1 2 x_3 x1=9−41x6−41x2−21x3
再将 x 1 x_1 x1代入其它每个式子中去,可以发现 S = 27 − 3 4 x 6 + 3 4 x 2 + 3 2 x 3 S=27-\frac 3 4 x_6+\frac 3 4 x_2+\frac 3 2x_3 S=27−43x6+43x2+23x3
如此转换,如果我们把S式中所有的x系数转为负数,就可以把常数项当做最优解。
这种把一个非基变量和基变量交换的操作叫做转轴(pivot)。
转轴操作可以把交换一个非基变量和一个基变量
设交换非基变量 x N x_N xN和基变量 x B x_B xB
x B = b i − ∑ j = 1 n a i j ⋅ x j x_B=b_i-\sum\limits_{j=1}^na_{ij}\cdot x_j xB=bi−j=1∑naij⋅xj
则转为 x N = ( b i − x B − ∑ j = 1 , j ≠ N n a i j ⋅ x j ) / a i N x_N=(b_i-x_B-\sum\limits_{j=1,j\ne N}^na_{ij}\cdot x_j)/a_{iN} xN=(bi−xB−j=1,j̸=N∑naij⋅xj)/aiN
上面的例子转轴 x 1 , x 6 x_1,x_6 x1,x6之后为
最大化: S = 27 − 3 4 x 6 + 3 4 x 2 + 3 2 x 3 S=27-\frac 3 4 x_6+\frac 3 4 x_2+\frac 3 2x_3 S=27−43x6+43x2+23x3
约束:
x 4 = 21 − 3 4 x 6 + 1 4 x 2 + 1 2 x 3 x 5 = 6 + 1 2 x 6 − 3 2 x 2 − 4 x 3 x 1 = 9 − 1 4 x 6 − 1 4 x 2 − 1 2 x 3 x j ≥ 0 ( j = 1 , 2 , 3 ) \begin{aligned} x_4 &= 21 − \frac 3 4 x_6 + \frac 1 4 x_2 + \frac 1 2 x_3\\ x_5 &= 6 +\frac 1 2 x_6− \frac 3 2 x_2 − 4x_3\\ x_1&=9-\frac 1 4 x_6-\frac 1 4 x_2-\frac 1 2 x_3\\ x_j&\geq 0\ (j=1,2,3) \end{aligned} x4x5x1xj=21−43x6+41x2+21x3=6+21x6−23x2−4x3=9−41x6−41x2−21x3≥0 (j=1,2,3)
可以发现S至少为27了
即不停的转轴,使得S式中所有的参数都为负。
每次寻找S中参数最大的 x j x_j xj(贪心的想,用最大的参数可以使答案扩得更大),然后从约束条件中,找到对 x j x_j xj限制最大的约束 i i i,即 b i a i j \frac {b_i} {a_{ij}} aijbi最小,转轴 x j x_j xj与 x i + n x_{i+n} xi+n(选择限制最大的约束,使得转轴后,其余约束条件中的 b i b_i bi非负)
当满足所有 b i ≥ 0 b_i\geq 0 bi≥0,显然可以把 x j = 0 , x i + n = b i x_j=0,x_{i+n}=b_i xj=0,xi+n=bi当做一个可行的初始解。
如果存在 b i < 0 b_i<0 bi<0,有两种方法
论文方法:
可以通过引入辅助变量 x 0 x_0 x0 ,经过转轴操作将 b i b_i bi转换为 ≥ 0 \geq 0 ≥0。
最大化 − x 0 -x_0 −x0
满足约束 x i + n = b i − ∑ j = 1 n a i j ⋅ x j + x 0 ( i = 1 , 2 , . . . , m ) x_{i+n}=b_i-\sum\limits_{j=1}^{n}a_{ij}\cdot x_j+x_0\ (i=1,2,...,m) xi+n=bi−j=1∑naij⋅xj+x0 (i=1,2,...,m)
如果得到最优解 x 0 = 0 x_0=0 x0=0,则这个 x 0 x_0 x0对原来问题的答案没有影响,否则原问题无解。
选择最小的 b i b_i bi,设为第k个约束,把那个约束中 x 0 x_0 x0进行转轴操作换出来,则
x 0 = − b k + x k + n + ∑ j = 1 n a k j ⋅ x j x_0=-b_k+x_{k+n}+\sum\limits_{j=1}^{n}a_{kj}\cdot x_j x0=−bk+xk+n+j=1∑nakj⋅xj
其它约束变为
x i + n = b i − b k + x k + n + ∑ j = 1 n ( a k j − a i j ) ⋅ x j x_{i+n}=b_i-b_k+x_{k+n}+\sum\limits_{j=1}^{n}(a_{kj}-a_{ij})\cdot x_j xi+n=bi−bk+xk+n+j=1∑n(akj−aij)⋅xj
因为 b k b_k bk为最小的b,所以这个式子中 b i − b k b_i-b_k bi−bk一定为非负数,从而把所有的约束中常数项变为非负。
此时再从要求最大化的式子中,选一个参数为正的 x p x_p xp,将其与 x 0 x_0 x0转轴。
重复这样的操作,直到所有 b i b_i bi全为正。
每次随机找到一个<0的 b i b_i bi,在从 b i b_i bi的约束条件中找一个系数大于0的 x j x_j xj,转轴 x i + n , x j x_{i+n},x_j xi+n,xj,就可以把当前 b i b_i bi变为正的(虽然有可能把其它条件的 b i b_i bi变为负数,所以要随机。。。)
多次执行随机操作,基本上可以把所有 b i b_i bi变为非负数。(UOJ拿97分)
把法二的 b i b_i bi,每次选择最小的负数 b i b_i bi来进行操作也能过,并不知道为什么。。。
UOJ179
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=55;
const double EPS=1e-8;
int dcmp(double a,double b)
{
if(fabs(a-b)<=EPS)
return 0;
return a<b?-1:1;
}
int n,m,type;
double A[MAXN][MAXN];
int id[MAXN],tp[MAXN];
void pivot(int r,int c)
{
swap(id[r+n],id[c]);
double tmp=-A[r][c];
A[r][c]=-1;
for(int j=0;j<=n;j++)
A[r][j]/=tmp;
for(int i=0;i<=m;i++)
if(i!=r&&dcmp(A[i][c],0)!=0)
{
tmp=A[i][c];
A[i][c]=0;
for(int j=0;j<=n;j++)
A[i][j]+=tmp*A[r][j];
}
}
void solve()
{
for(int i=1;i<=n;i++)
id[i]=i;
for(;;)
{
int i=0,j=0;
for(int k=1;k<=m;k++)
if(dcmp(A[k][0],0)==-1&&(!i||rand()&1))
i=k;
if(i==0)
break;
for(int k=1;k<=n;k++)
if(dcmp(A[i][k],0)==1&&(!j||rand()&1))
j=k;
if(j==0)
{
puts("Infeasible");
return;
}
pivot(i,j);
}
for(;;)
{
int i=0,j=0;
double mx=0,mn=1e9;
for(int k=1;k<=n;k++)
if(dcmp(A[0][k],mx)==1)
mx=A[0][k],j=k;
if(j==0)
break;
for(int k=1;k<=m;k++)
if(dcmp(A[k][j],0)==-1&&dcmp(-A[k][0]/A[k][j],mn)==-1)
mn=-A[k][0]/A[k][j],i=k;
if(i==0)
{
puts("Unbounded");
return;
}
pivot(i,j);
}
printf("%.9f\n",A[0][0]);
for(int i=n+1;i<=n+m;i++)
tp[id[i]]=i-n;
if(type)
for(int i=1;i<=n;i++)
printf("%.9f%c",tp[i]?A[tp[i]][0]:0," \n"[i==n]);
}
int main()
{
scanf("%d%d%d",&n,&m,&type);
for(int i=1;i<=n;i++)
scanf("%lf",&A[0][i]);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%lf",&A[i][j]);
A[i][j]*=-1;
}
scanf("%lf",&A[i][0]);
}
solve();
return 0;
}