BZOJ 4004 JLOI2015 装备购买 高斯消元+线性基
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4004
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
一行两个数,第一个数表示能够购买的最多装备数量,第二个数表示在购买最多数量的装备的情况下的最小花费。
Sample Input
3 3
1 2 3
3 4 5
2 3 4
1 1 2
1 2 3
3 4 5
2 3 4
1 1 2
Sample Output
2 2
HINT
如题目中描述,选择装备 1 装备 2,装备 1 装备 3,装备 2 装备 3 均可,但选择装备 1 和装备 2 的花费最小,为 2。
对于 100% 的数据:1 <= n;m <= 500,0 <= aj <= 1000.
——————————————————————————————————————————————————————
题意概述:
·给出N个M维向量,选择向量i花费代价ci。求一个包含向量最多的线性无关组,使得选择这个无关组的代价最小。
·N,M<=500,ai<=1000(话说ci呢?)
分析:
·可以把向量看成一个多元一次方程。如果一些方程相关,那么这些方程可以互相表示。
·考虑高斯消元过程,发现最终系数为0的方程能够被上面的一些方程表示出来,换言之不为0的向量一旦和这些向量相组合就不是线性无关,不符合要求。最终高斯消元剩下的非0的方程数量就是这个集合中的线性无关组数量。即一个向量集和的线性不相关向量数量是唯一确定的,并且和高斯消元后非0向量的数量相同。(可以YY两个线性相关向量集合在一起变成一个新集合的情况)
·解决了最大购买数的问题,那么最小代价?
·贪心,把所有的向量按照权值从小到大排序,然后直接消元,遇到当前向量关键维度的值为0的时候选择还没有考虑的向量中权值最小的那个作为现在的关键字,延后考虑当前向量。有了上面第一问的分析之后这个贪心显然是正确的。具体实现搞个链表什么的。
·最坑的地方还是精度......最后看精度没救了强行上了逆元来进行模意义下的运算,然而这好像就步入了玄学的领域......为了不冲突就只能在比较大的mo意义下搞事情然而有点慢啊......
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 const int maxn=505; 14 const int mo=1000000007; 15 typedef long long LL; 16 17 int N,M,C[maxn],next[maxn]; 18 struct data{ 19 int id,v; 20 friend bool operator < (data x,data y){ 21 return x.v<y.v; 22 } 23 }D[maxn]; 24 int A[maxn][maxn]; 25 26 void data_in() 27 { 28 scanf("%d%d",&N,&M); 29 int x; 30 for(int i=1;i<=N;i++) 31 for(int j=1;j<=M;j++) 32 scanf("%d",&A[i][j]); 33 for(int i=1;i<=N;i++) scanf("%d",&C[i]); 34 } 35 void exgcd(LL a,LL b,LL &d,LL &x,LL &y) 36 { 37 if(!b) d=a,x=1,y=0; 38 else exgcd(b,a%b,d,y,x),y-=(a/b)*x; 39 } 40 int inv(int a) 41 { 42 LL x=0,y=0,d=0; exgcd(a,mo,d,x,y); 43 return x; 44 } 45 int Gauss() 46 { 47 int p=next[0],i=1,last=0; 48 while(p&&i<=M){ 49 if(!A[p][i]){ 50 int pp,_last=p; 51 for(pp=next[p];pp;_last=pp,pp=next[pp]) if(A[pp][i]) break; 52 if(!pp){ i++; continue; } 53 next[_last]=next[pp],next[last]=pp,next[pp]=p; 54 p=pp; 55 } 56 for(int pp=next[p];pp;pp=next[pp]){ 57 int t=1ll*A[pp][i]*inv(A[p][i])%mo; 58 for(int j=i;j<=M;j++) 59 A[pp][j]=(A[pp][j]-1ll*A[p][j]*t%mo+mo)%mo; 60 } 61 last=p,p=next[p],i++; 62 } 63 return p; 64 } 65 void work() 66 { 67 for(int i=1;i<=N;i++) D[i]=(data){i,C[i]}; 68 sort(D+1,D+N+1); 69 int p=0; 70 for(int i=1;i<=N;i++) next[p]=D[i].id,p=D[i].id; 71 next[p]=0; 72 int P=Gauss(),ans1=0,ans2=0; 73 for(p=next[0];p!=P;p=next[p]) ans1++,ans2+=C[p]; 74 printf("%d %d\n",ans1,ans2); 75 } 76 int main() 77 { 78 data_in(); 79 work(); 80 return 0; 81 }