bzoj 4004 : 装备购买(线性基)

在这里插入图片描述分析:题目很裸,最大购买个数就是线性无关向量的个数。要使得花钱最少,可以套用贪心的思想。

反思:犯了一个错误,学了高斯消元后想在高斯消元的过程中得到解,但在高斯消元的过程中贪心并不是正解,似乎会影响到后面的决策使得总花费不是最优。这题要用线性基,线性基求基的过程是枚举每一个向量,看这个向量能加到哪一行,如果当前行向量已存在那么对这个向量进行初等变换使得向量该位置为0,这样会保证线性基的每一个向量的最左端不为0的位置不同,最终加进去的各个向量线性无关。(学过线性代数会发现这就是模拟求矩阵的秩的过程,每次加入一个和已经得到的向量组线性无关的向量来得到一个更大的线性无关向量组,最终加不可加时就得到了极大线性无关向量组)

花费最小:考虑贪心,将向量按价格从小到大排序。总是先将价格最低的向量加入到线性无关向量组中,设某一行加入的向量并不是当前花费最小的向量,那么用这个花费最小的向量代替所选择的这个向量仍然可以构成一个线性基,它们的线性空间相同,而总花费更小。

关于精度问题:这题卡了精度,eps开到几都没用,查了一下黑科技就是使用求模意义下的指,只要模数够大就不会影响答案。这题可以用 1 0 9 + 7 10^9 + 7 109+7作为模数。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e2 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
int n,m;
ll a[maxn][maxn];
int p[maxn],c[maxn],b[maxn];
bool cmp(int a,int b) {
	return c[a] < c[b];
}
ll fpow(ll a,ll b) {
	ll r = 1;
	while(b) {
		if(b & 1) r = r * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return r;
}
int main() {
	memset(b,0,sizeof b);
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			scanf("%lld",&a[i][j]);
	for(int i = 1; i <= n; i++) {
		scanf("%d",&c[i]);
		p[i] = i;
	}
	sort(p + 1,p + n + 1,cmp);
	int ans = 0,res = 0;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			if(a[p[i]][j]) {
				if(!b[j]) {
					b[j] = p[i];
					ans += c[p[i]];
					res++;
					break;
				}
				else {
					ll rate = a[p[i]][j] * fpow(a[b[j]][j],mod - 2) % mod;
					for(int k = 1; k <= m; k++)
						a[p[i]][k] = (a[p[i]][k] - a[b[j]][k] * rate % mod + mod) % mod;
				}
				
			}
		}
	}
	printf("%d %d\n",res,ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值