装备购买[BZOJ4004]

欢迎大家访问我的老师的OJ———caioj.cn

题面描述

传送门

思路

提要:

做此题时,请务必了解线性空间的基以及矩阵的秩。

n n n件装备看作 n n n个长度为 m m m的向量,

根据题意,购买的装备对应的向量应该是线性无关的(要满足向量不能被其他向量表出)。

要买下最多数量的装备,其实就是求线性空间的任意一个基的个数。

a i . j ( 1 ≤ i ≤ n , 1 ≤ j ≤ m ) a_{i.j}(1\le i\le n,1\le j\le m) ai.j(1in,1jm)看作系数矩阵,每个装备 z i z_i zi都是一个行向量。

用高斯消元求出该矩阵的秩(相当于线性空间的基中元素个数),就得到了能买下的装备的最多数量。

本题还需要求花最少的钱。我们只需要在高斯消元的过程中,使用贪心策略,对于每一个主元 x i x_i xi,在前 i − 1 i-1 i1列为 0 0 0、第 i i i列不为 0 0 0的行向量中,选择价格最低的一个,小区其他行中i列的值。

用反证法证明。假设花费价钱最少的基底(简称基) z [ i 1 ] , z [ i 2 ] , z [ i 3 ] , . . . . . . , z [ i p ] z[i_1],z[i_2],z[i_3],......,z[i_p] z[i1],z[i2],z[i3],......,z[ip]不包含价格最少的行向量 z [ k ] z[k] z[k],因为基底是极大线性无关子集,所以 z [ k ] z[k] z[k]能被 z [ i 1 ] , z [ i 2 ] , z [ i 3 ] , . . . . . . , z [ i p ] z[i_1],z[i_2],z[i_3],......,z[i_p] z[i1],z[i2],z[i3],......,z[ip]表出,不妨设 z [ k ] z[k] z[k]= b 1 z [ i 1 ] + b 2 z [ i 2 ] + b 3 z [ i 3 ] + . . . . . . + b p z [ i p ] b_1z[i_1]+b_2z[i_2]+b_3z[i_3]+......+b_pz[i_p] b1z[i1]+b2z[i2]+b3z[i3]+......+bpz[ip].

移向就有 z [ i p ] = ( z [ k ] − b 1 z [ i 1 ] − b 2 z [ i 2 ] − b 3 z [ i 3 ] − . . . . . . − b p − 1 z [ i p − 1 ] ) / b p z[i_p]=(z[k]-b_1z[i_1]-b_2z[i_2]-b_3z[i_3]-......-b_{p-1}z[i_{p-1}])/b_p z[ip]=(z[k]b1z[i1]b2z[i2]b3z[i3]......bp1z[ip1])/bp,即 z [ i p ] z[i_p] z[ip]能被 z [ k ] z[k] z[k] z [ i 1 ] , z [ i 2 ] , z [ i 3 ] , . . . . . . , z [ i p − 1 ] z[i_1],z[i_2],z[i_3],......,z[i_{p-1}] z[i1],z[i2],z[i3],......,z[ip1]表出。

所以这两个基都能表出相同的线性空间,而含有 z [ k ] z[k] z[k]明显总费用最低,与假设矛盾。

证毕。

AC code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define eps 1e-6
using namespace std;
long double a[510][510];
int n,ans,m,dim,c[510];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			double tmp;scanf("%lf",&tmp);a[i][j]=tmp;
		}
	for(int i=1;i<=n;i++)scanf("%d",&c[i]);
	dim=0;ans=0;
	for(int i=1;i<=m;i++)
	{
		int x=0;
		for(int j=dim+1;j<=n;j++)
			if(fabs(a[j][i])>eps&&(x==0||c[x]>c[j]))x=j;
		if(x==0)continue;
		ans+=c[x];dim++;
		for(int j=dim;j<=m;j++)swap(a[dim][j],a[x][j]);
		swap(c[x],c[dim]);
		for(int k=dim+1;k<=n;k++)
		{
			if(fabs(a[k][i])>eps)
			{
				for(int j=m;j>=i;j--)a[k][j]-=a[k][i]/a[dim][i]*a[dim][j];
			}
		}
	}	
	printf("%d %d\n",dim,ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值