大致题意: 给定\(n\)个\(m\)维向量,每个向量有一个代价。规定若一个向量能用所持向量以任意系数相加得到,则这个向量就不必选择。求选择的最大向量数目以及此时所花的最小代价。
线性基
如果你对线性基的认知仅仅停留在异或的层面,那么这道题就无比棘手了。
事实上,异或的线性基可以看作特殊的线性基,把一个数分成一个向量,向量的每维只有\(0/1\)两种数。(类比\(01\)矩阵,可以叫做\(01\)线性基?)
那么一般的线性基该如何维护呢?这个过程实际上类似于高斯消元,大概可以看作异或线性基和高斯消元的结合,实现可以详见代码。
贪心
因此,对于这道题,我们贪心地对向量按代价排序,每次判断能否插入线性基,能插就插。
至于贪心的依据,我相信是比较显然的,这里就不予证明了。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500
#define eps 1e-8
#define DB long double
using namespace std;
int n,m;
struct Vec//存储一个向量
{
int v;DB a[N+5];I DB& operator [] (CI x) {return a[x];}
I bool operator < (Con Vec& o) Con {return v<o.v;}//按代价排序
}s[N+5];
class LinearBasis//线性基
{
private:
int p[N+5];Vec v[N+5];
public:
I bool Ins(Vec x)//插入向量
{
RI i,j;DB t;for(i=1;i<=m;++i) if(fabs(x[i])>eps)//如果这一位不为0
{
if(!p[i]) {for(p[i]=1,j=i;j<=m;++j) v[i][j]=x[j];return 1;}//如果这一位还没有向量,插入成功
for(t=-x[i]/v[i][i],j=i;j<=m;++j) x[j]+=t*v[i][j];//类似于高斯消元,将这一位消去
}return 0;//插入失败
}
}B;
int main()
{
RI i,j;for(scanf("%d%d",&n,&m),i=1;i<=n;++i) for(j=1;j<=m;++j) scanf("%Lf",&s[i][j]);
for(i=1;i<=n;++i) scanf("%d",&s[i].v);sort(s+1,s+n+1);
RI t1=0,t2=0;for(i=1;i<=n;++i) B.Ins(s[i])&&(++t1,t2+=s[i].v);//贪心
return printf("%d %d\n",t1,t2),0;
}