装备购买
讲一下我的做法,不按照行向量考虑而是按照列向量考虑,将所有的\(z\)全部变成列向量然后进行初等行变换
我们先不管\(z\)的顺序,直接进行初等行变换,最后化出来一个行简化梯形矩阵,很显然的一个极大无关组就是每个非零行的第一个非零元所在的列(也就是非自由元所在的列)
于是一个很自然的想法就是我们先将所有\(z\)按照花费从小到大排序,然后进行初等行变换,最后按照上述的方法选择就是最优的方案
证明:最终的向量组一定包含花费最低的向量,否则的话花费最低的向量可以被极大无关组表出,而且系数不全为\(0\),于是极大无关组的某个向量就可以被替换为这个花费最低的向量,并且可以知道,替换之前的极大无关组与替换之后的向量组等价,于是替换之后的向量组也是极大无关组;然后利用数学归纳法,假设我们现在的行简化梯形矩阵长成这个样子
现在在考虑第\(j\)列
如果第\(j\)列元素全为\(0\)了(指梯形下面的元素),那么这个列向量肯定不选,因为已经可以被前面选择的向量表出了(注意初等行变换不改变列向量之间的线性关系);否则的话,这个向量一定要选,可以利用上面类似的反证法证明
这道题目还卡精度。如果用double,精度不能太高,eps=1e-4,如果用long double,精度不能太低,eps=1e-8
然后解释一下蓝书上的做法
仔细想一下对行向量进行初等行变换会发生什么。不难发现(模拟一遍过程),假设我们当前在第\(i\)行,前面\(i-1\)行已经确定了,那么第一行的向量就是最开始给出的某个向量\(a[j_1]\),第二行的向量是\(a[j_1]\)与最开始给出的向量中的另一个向量\(a[j_2]\)的线性组合,第三行的向量是\(a[j_1],a[j_2]\)与最开始给出的向量中的另一个向量\(a[j_3]\)的线性组合,依次类推。那么此时我们在找第\(i\)个线性无关的向量(假设我们前面找到的\(i-1\)个向量都是线性无关的,我们找到了当前矩阵的第\(k\)行(注意当前矩阵的第\(k\)行的向量是\(a[j_1],a[j_2],...,a[j_{i-1}]\)与最开始给出的向量中的另一个向量\(a[j_i]\)的线性组合),由于第\(k\)行向量的当前列不为\(0\),所以这个向量与我们已经找到的前\(i-1\)个向量一定是线性无关的(我们就将前\(i-1\)个向量与这个向量提取出来放在一起做初等行变换,最终结果肯定不变,而且每一行都是非零向量,所以满秩,所以线性无关),于是我们按照蓝书的做法最终得到的肯定是线性无关组,而且肯定是花费的最少的(花费的证明按照我的做法的证明证)
代码仓库的代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
long double a[510][510], eps = 1e-8;
int c[510], n, m, dim, ans;
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
double temp; scanf("%lf", &temp); a[i][j] = temp;
}
for (int i = 1; i <= n; i++) {
double temp; scanf("%lf", &temp); c[i] = temp;
}
int dim = 0;
// 对每个未知量xi进行一次消元
for (int i = 1; i <= m; i++) {
// 找到xi系数不为0、价格最低的一个
int now = 0;
for (int j = dim + 1; j <= n; j++) {
if (fabs(a[j][i]) > eps && (now == 0 || c[j] < c[now]))
now = j;
}
// xi是自由元
if (now == 0) continue;
// 向基底中增加一个向量
dim++;
ans += c[now];
for (int j = 1; j <= m; j++)
swap(a[now][j], a[dim][j]);
swap(c[now], c[dim]);
// 消去其它行中第i列的值
for (int j = 1; j <= n; j++)
if (dim != j && fabs(a[j][i]) > eps) {
long double rate = a[j][i] / a[dim][i];
for (int k = i; k <= m; k++)
a[j][k] -= a[dim][k] * rate;
}
}
cout << dim << ' ' << ans << endl;
}