1. 程式人生 > 其它 >【ybt金牌導航8-2-5】【luogu P3265】【bzoj 4004】裝備購買(貪心)(實數線性基)(高斯消元)

【ybt金牌導航8-2-5】【luogu P3265】【bzoj 4004】裝備購買(貪心)(實數線性基)(高斯消元)

給你 n 個物品,每個物品有價格,和它的特徵向量。 然後如果有一個東西可以通過某幾個你已經買了的物品向量每一位乘各自各自的一個實數相加得到,那你就不可以買這個東西。(一個物品可以選實數,然後每一位都要乘這個) 然後問你最多能買多少個東西,在買最多東西的前提下最少要多少錢。

裝備購買

題目連結:ybt金牌導航8-2-5 / luogu P3265 / bzoj 4004

題目大意

給你 n 個物品,每個物品有價格,和它的特徵向量。
然後如果有一個東西可以通過某幾個你已經買了的物品向量每一位乘各自各自的一個實數相加得到,那你就不可以買這個東西。(一個物品可以選實數,然後每一位都要乘這個)
然後問你最多能買多少個東西,在買最多東西的前提下最少要多少錢。

思路

首先我們可以通過各種各樣的觀察性質:
如果一些物品能湊出一個物品,那這一些物品中的任何一個都能被這些物品中剩下的和這個湊出。
(那物品數好像怎麼搞都差不多)

那我們不難想到貪心,從價格小的開始搞。
那問題就變成每次判斷能不能放了。

然後你會發現它這個湊的過程好像高斯消元的式子,然後你會發現你頂對是 \(n\) 個,因為你互補抵消的可以高斯消元中變成每個確定了一位向量的“值”。

然後你就考慮一個很神奇的東西叫做實數線性基(其中用高斯消元輔助實現)。
其實就是類似普通的線性基,我們每次不斷的找位置,找到自己這一位非 \(0\)(注意精度問題,\(1e-6\) 是不行的,\(1e-5\) 就可以了,十分玄學),而且沒有別的人佔了的位置佔據。
如果找到的位置別人佔據了,那原來是異或,這裡就是用高斯消元把這一位消掉。

然後這麼搞就可以了。

程式碼

#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;
}