洛谷 P3265 [JLOI2015]装备购买

给定 \(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;
}

猜你喜欢

转载自www.cnblogs.com/natsuka/p/12653603.html