【ybt金牌导航8-2-5】【luogu P3265】【bzoj 4004】装备购买(贪心)(实数线性基)(高斯消元)

装备购买

题目链接:ybt金牌导航8-2-5 / luogu P3265 / bzoj 4004

题目大意

给你 n 个物品,每个物品有价格,和它的特征向量。
然后如果有一个东西可以通过某几个你已经买了的物品向量每一位乘各自各自的一个实数相加得到,那你就不可以买这个东西。(一个物品可以选实数,然后每一位都要乘这个)
然后问你最多能买多少个东西,在买最多东西的前提下最少要多少钱。

思路

首先我们可以通过各种各样的观察性质:
如果一些物品能凑出一个物品,那这一些物品中的任何一个都能被这些物品中剩下的和这个凑出。
(那物品数好像怎么搞都差不多)

那我们不难想到贪心,从价格小的开始搞。
那问题就变成每次判断能不能放了。

然后你会发现它这个凑的过程好像高斯消元的式子,然后你会发现你顶对是 n n n 个,因为你互补抵消的可以高斯消元中变成每个确定了一位向量的“值”。

然后你就考虑一个很神奇的东西叫做实数线性基(其中用高斯消元辅助实现)。
其实就是类似普通的线性基,我们每次不断的找位置,找到自己这一位非 0 0 0(注意精度问题, 1 e − 6 1e-6 1e6 是不行的, 1 e − 5 1e-5 1e5 就可以了,十分玄学),而且没有别的人占了的位置占据。
如果找到的位置别人占据了,那原来是异或,这里就是用高斯消元把这一位消掉。

然后这么搞就可以了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

struct node {
	double f[505];
	int c;
}a[505];
int n, m, p[505];
int num, ans;
double eps = 1e-5;

bool cmp(node x, node y) {
	return x.c < y.c;
}

double Abs(double x) {
	return (x < 0) ? -x : x;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++) scanf("%lf", &a[i].f[j]);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i].c);
	
	sort(a + 1, a + n + 1, cmp);//贪心
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			if (Abs(a[i].f[j]) <= eps) continue;//线性基
			if (!p[j]) {
				p[j] = i; num++; ans += a[i].c;
				break;
			}
			double tmp = a[i].f[j] / a[p[j]].f[j];//高消来弄
			for (int k = j; k <= m; k++) {
				a[i].f[k] -= tmp * a[p[j]].f[k];
			}
		}
	}
	
	printf("%d %d", num, ans);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值