给定 \(n\) 个 \(m\) 维向量,其中第 \(i\) 个向量的花费为 \(p_i\),求一组个数长度最大、花费最小的线性无关向量。
一道线性基裸题。
高中同学都知道,所有 \(m\) 维向量都可以用 \(m\) 对线性无关的基底线性表示。只要用高斯消元添加就好了。
关于费用最小,可以排序后贪心插入。可以证明这是最优解。
#include <cstdio>
#include <algorithm>
#include <cstring>
typedef long double db;
const int MAXN = 5e2 + 19;
const db EPS = 1e-8;
struct Vector{
db e[MAXN];
int p;
Vector(){
std::memset(e, 0, sizeof e);
}
bool operator<(const Vector& b)const{
return p < b.p;
}
Vector operator-=(const Vector& b){
for(int i = 1; i < MAXN; ++i)
e[i] -= b.e[i];
return *this;
}
friend Vector linearmul(Vector, db);
}arm[MAXN];
Vector linearmul(Vector a, db b){
for(int i = 1; i < MAXN; ++i)
a.e[i] *= b;
return a;
}
int n, m, ans, cnt;
inline db fabs(db b){
return b < 0 ? -b : b;
}
class LinearBase{
private:
Vector e[MAXN];
public:
bool insert(Vector ist){
for(int i = 1; i <= m; ++i){
if(fabs(ist.e[i]) < EPS)
continue;
if(fabs(e[i].e[i]) < EPS){
e[i] = ist;
return true;
}
else
ist -= linearmul(e[i], ist.e[i] / e[i].e[i]);
}
return false;
}
}myBase;
int main(){
std::scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
std::scanf("%Lf", &arm[i].e[j]);
for(int i = 1; i <= n; ++i)
std::scanf("%d", &arm[i].p);
std::sort(arm + 1, arm + 1 + n);
for(int i = 1; i <= n; ++i)
if(myBase.insert(arm[i]))
ans += arm[i].p, ++cnt;
std::printf("%d %d\n", cnt, ans);
return 0;
}