
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/theArcticOcean/article/details/50636716
松弛变量:引入新的变量,且保持变量间的关系不变。
提到线性规划,自己最先想起的是高中的各种数学题。
线性规划问题的标准形式:
用松弛变量技术把不等式化成等式:
从一个基本可行解出发,寻找能使得目标函数更优的另一个解,迭代下去直到找不到。使用这种方法避免大量的无用比较。
|
I |
II |
|
设备 原材料A 原材料B |
1 4 0 |
2 0 4 |
8台时 16kg 12kg |
所以:
称Z表达式中的变量为非基变量(x1,x2),约束方程中剩余的变量是基变量 (x3,x4,x5)
当非基变量全是0时,Z有一个X解。这一个解作为初始解。想要让Z变大,那么正整数系数的变量一定要变大。
可以选一个最大系数的变量x2,那么x1随着x2变大将变成0,那么从约束方程可以看出,X5先变成0,故把X1和X5作为新的非基变量。
有:
存在正系数说明目标函数还能变大。令x5=0,从约束方程可以看出,x1最大取2,此时x3=0。那么新的非基变量就是x3, x5.于是得到:
重复刚刚的步骤,得到:
当x3=x4=0时得到最优解z=14
把刚刚的过程表格化:(表格化思想来自《组合数学及应用》)
我们发现,事实上,一个基本变量和一个非基本变量互换后得到了下一个方程组。
寻找互换的非基本变量:对应的Z行系数是负数的,选择它,有多个负数就选择最小的那个。对应的非基本变量就是选择项。
寻找互换的基本变量:非基本变量增大,基本变量减小,谁先变成0,谁就是基本变量。
数值元素变化规律:称参与互换的非基变量所在列是主元列,基本变量所在行是主元行。
设当前的主元行是r,主元列是c,主元为
设投影列元素a,投影行元素b。投影是相对主元行和主元列而言,上图中16的投影行元素是12,投影列元素是0
那么其他的元素就是:
后续变化:
嗯嗯,就是这个流程。
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const int maxm = 500; // 约束数目上限 const int maxn = 500; // 变量数目上限 const double INF = 1e100; const double eps = 1e-10; struct Simplex { int n; // 变量个数 int m; // 约束个数 double a[maxm][maxn]; // 输入矩阵 int B[maxm], N[maxn]; // 算法辅助变量 void calc(int r, int c) { swap(N[c], B[r]); a[r][c] = 1 / a[r][c]; for(int j = 0; j <= n; j++) if(j != c) a[r][j] *= a[r][c]; for(int i = 0; i <= m; i++) if(i != r) { for(int j = 0; j <= n; j++) if(j != c) a[i][j] -= a[i][c] * a[r][j]; a[i][c] = -a[i][c] * a[r][c]; } } bool check() { while(1) { int r, c; double p = INF; for(int i = 0; i < m; i++){ if(a[i][n] < p) { r=i; p = a[r][n]; // 最右边的常数列 } } if(p > -eps) return true; // 都是正数的话可行。 p = 0; for(int i = 0; i < n; i++) { if(a[r][i] < p){ c=i; p = a[r][c]; // 找最小列 } } if(p > -eps) return false; // r对应的所有列都是正数,没有负数,无解。 p = a[r][n]/a[r][c]; for(int i = r+1; i < m; i++) if(a[i][c] > eps) { double v = a[i][n] / a[i][c]; if(v < p) { r = i; p = v; } } calc(r, c); } } // 解有界返回1,无解返回0,无界返回-1。b[i]为x[i]的值,ret为目标函数的值 int simplex(int n, int m, double x[maxn], double& ret) { this->n = n; this->m = m; for(int i = 0; i < n; i++) N[i] = i; for(int i = 0; i < m; i++) B[i] = n+i; if(!check()) return 0; while(1){ int r, c; double p = 0; for(int i = 0; i < n; i++) if(a[m][i] > p) { c = i; p = a[m][c]; } if(p < eps) { for(int i = 0; i < n; i++) if(N[i] < n) x[N[i]] = 0; for(int i = 0; i < m; i++) if(B[i] < n) x[B[i]] = a[i][n]; ret = -a[m][n]; return 1; } p = INF; for(int i = 0; i < m; i++) if(a[i][c] > eps) { double v = a[i][n] / a[i][c]; if(v < p) { r = i; p = v; } } if(p == INF) return -1; calc(r, c); } } }; Simplex solver; int main() { //freopen("cin.txt","r",stdin); int n, m; while(~scanf("%d%d", &n, &m)) { for(int i = 0; i < n; i++) scanf("%lf", &solver.a[m][i]); // 目标函数 solver.a[m][n] = 0; for(int i = 0; i < m; i++) for(int j = 0; j < n+1; j++) scanf("%lf", &solver.a[i][j]); double ans,x[maxn]; solver.simplex(n,m,x,ans); ans *= m; //printf("Nasa can spend %d taka.\n", int(ans+0.5)); // 至少花费的:3.008-->4 printf("Nasa can spend %d taka.\n", (int)floor(ans + 1 - eps)); } return 0; }
- 上一篇 MySQL 时间和日期函数
- 下一篇 PHP和Mysql简单的交互