【bzoj4004】【JLOI2015】装备购买 (线性基+高斯消元)
Description
脸哥最近在玩一款神奇的游戏,这个游戏里有 n 件装备,每件装备有 m 个属性,用向量zi(aj ,.....,am) 表示 (1 <= i <= n; 1 <= j <= m),每个装备需要花费 ci,现在脸哥想买一些装备,但是脸哥很穷,所以总是盘算着怎样才能花尽量少的钱买尽量多的装备。对于脸哥来说,如果一件装备的属性能用购买的其他装备组合出(也就是说脸哥可以利用手上的这些装备组合出这件装备的效果),那么这件装备就没有买的必要了。严格的定义是,如果脸哥买了 zi1,.....zip这 p 件装备,那么对于任意待决定的 zh,不存在 b1,....,bp 使得 b1zi1 + ... + bpzip = zh(b 是实数),那么脸哥就会买 zh,否则 zh 对脸哥就是无用的了,自然不必购买。举个例子,z1 =(1; 2; 3);z2 =(3; 4; 5);zh =(2; 3; 4),b1 =1/2,b2 =1/2,就有 b1z1 + b2z2 = zh,那么如果脸哥买了 z1 和 z2 就不会再买 zh 了。脸哥想要在买下最多数量的装备的情况下花最少的钱,你能帮他算一下吗?
Input
第一行两个数 n;m。接下来 n 行,每行 m 个数,其中第 i 行描述装备 i 的各项属性值。接下来一行 n 个数,
其中 ci 表示购买第 i 件装备的花费。
Output
一行两个数,第一个数表示能够购买的最多装备数量,第二个数表示在购买最多数量的装备的情况下的最小花费
题解:
这乍一看,似乎没有什么规律,但是,仔细一想,这题跟线性基有点像。
普通的异或线性基是将十进制数转为二进制,将 01 串作为向量,加入线性基,这道题将一个十进制的向量插入线性基。
我们可以用高斯消元消去最高位替代异或,如下:
1 bool insert(Vector x){ 2 for(int i=m;i>=1;i--){ 3 if(fabs(x.v[i])<eps)continue; 4 if(a[i].empty()){a[i]=x;return true;} 5 double k=x.v[i]/a[i].v[i]; 6 for(int j=1;j<=m;j++)x.v[j]-=a[i].v[j]*k; 7 if(x.empty())return false; 8 } 9 }
这同样满足线性基的性质,(感性理解)
COMPLETE CODE:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 using namespace std; 7 8 #define eps 1e-5 9 int n,m,x,sum,ans; 10 struct Vector{ 11 double v[505]; 12 int c; 13 bool operator<(const Vector &b)const{ 14 return c<b.c; 15 } 16 bool empty(){ 17 for(int i=1;i<=m;i++) 18 if(fabs(v[i])>=eps)return false; 19 return true; 20 } 21 }a[505]; 22 struct LB{ 23 Vector a[505]; 24 bool insert(Vector x){ 25 for(int i=m;i>=1;i--){ 26 if(fabs(x.v[i])<eps)continue; 27 if(a[i].empty()){a[i]=x;return true;} 28 double k=x.v[i]/a[i].v[i]; 29 for(int j=1;j<=m;j++)x.v[j]-=a[i].v[j]*k; 30 if(x.empty())return false; 31 } 32 } 33 }lb; 34 35 int main(){ 36 scanf("%d%d",&n,&m); 37 for(int i=1;i<=n;i++) 38 for(int j=1;j<=m;j++)scanf("%d",&x),a[i].v[j]=x; 39 for(int i=1;i<=n;i++)scanf("%d",&a[i].c); 40 sort(a+1,a+n+1); 41 for(int i=1;i<=n;i++) 42 if(lb.insert(a[i]))sum++,ans+=a[i].c; 43 printf("%d %d",sum,ans); 44 }
注意!! 这题卡精度!!