题目链接: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意义下搞事情然而有点慢啊......
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int maxn=;
const int mo=;
typedef long long LL; int N,M,C[maxn],next[maxn];
struct data{
int id,v;
friend bool operator < (data x,data y){
return x.v<y.v;
}
}D[maxn];
int A[maxn][maxn]; void data_in()
{
scanf("%d%d",&N,&M);
int x;
for(int i=;i<=N;i++)
for(int j=;j<=M;j++)
scanf("%d",&A[i][j]);
for(int i=;i<=N;i++) scanf("%d",&C[i]);
}
void exgcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(!b) d=a,x=,y=;
else exgcd(b,a%b,d,y,x),y-=(a/b)*x;
}
int inv(int a)
{
LL x=,y=,d=; exgcd(a,mo,d,x,y);
return x;
}
int Gauss()
{
int p=next[],i=,last=;
while(p&&i<=M){
if(!A[p][i]){
int pp,_last=p;
for(pp=next[p];pp;_last=pp,pp=next[pp]) if(A[pp][i]) break;
if(!pp){ i++; continue; }
next[_last]=next[pp],next[last]=pp,next[pp]=p;
p=pp;
}
for(int pp=next[p];pp;pp=next[pp]){
int t=1ll*A[pp][i]*inv(A[p][i])%mo;
for(int j=i;j<=M;j++)
A[pp][j]=(A[pp][j]-1ll*A[p][j]*t%mo+mo)%mo;
}
last=p,p=next[p],i++;
}
return p;
}
void work()
{
for(int i=;i<=N;i++) D[i]=(data){i,C[i]};
sort(D+,D+N+);
int p=;
for(int i=;i<=N;i++) next[p]=D[i].id,p=D[i].id;
next[p]=;
int P=Gauss(),ans1=,ans2=;
for(p=next[];p!=P;p=next[p]) ans1++,ans2+=C[p];
printf("%d %d\n",ans1,ans2);
}
int main()
{
data_in();
work();
return ;
}