[JLOI2015]装备购买

题意简述

给出 \(n\)\(m\) 维的向量,每个向量有一个花费,问最多有多少个向量线性无关(即不能通过其中某几个向量表示出另一个向量),并在满足数量最多的情况下使花费最小。

思路

这题一看就很线性代数。

把向量 \(\vec a\) 看做 \(m\) 维的坐标系上一个起点为原点,终点坐标为该向量元素的箭头。由于系数 \(b\) 为实数,所以 \(b\cdot\vec a\) 所表示的空间是 \(\vec a\) 所在的连续直线。

可以确定的是:只需要 \(m\) 个线性无关的向量就可以表示出所有 \(m\) 维的向量。因为可以把这 \(m\) 个向量当做 \(m\) 维坐标系的坐标轴。那么如何找到这 \(m\) 个向量呢?解法是把这些向量用高斯消元。

首先,对这些向量进行相加减或把一个向量乘以一个常数(也就是对向量进行初等行变换),这些向量张成的空间不变,所以高斯消元的正确性可以保证。在高斯消元的过程中,会把这个矩阵消成上阶梯型矩阵,这样消下来的每一个非空向量都能表示独立的一维,也就是说消完以后的每一行非零向量都有用。所以我们只用高斯消元一遍,消成上阶梯型矩阵之后,找出所有不是全零的向量,答案即为这些向量的个数和费用之和。

至于如何使费用最小,只需要在消元前对所有向量按费用从小到大排个序即可。因为高斯消元的时候会先使用前面的向量,所以最后得到的结果一定是最优的。

代码

#include<bits/stdc++.h>
#define MAXN 510
#define eps 1e-7
using namespace std;
int n,m,c[MAXN],cnt,ans,sw;
struct rin
{
    int c;
    long double a[MAXN];
}rr[MAXN];
bool cmp(rin a,rin b)
{
    return a.c<b.c;
}
bool flag=0;
long double pr,di;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%LF",&rr[i].a[j]);
    for(int i=1;i<=n;i++)
        scanf("%d",&rr[i].c);
    sort(rr+1,rr+n+1,cmp);
    for(int i=1;i<=min(m,n);i++)
    {
        sw=i;
        while(sw<=n&&abs(rr[sw].a[i])<=eps)sw++;
        swap(rr[sw],rr[i]);
        if(abs(rr[i].a[i])<=eps)continue;
        di=rr[i].a[i];
        for(int j=i;j<=m;j++)
            rr[i].a[j]/=di;
        for(int j=i+1;j<=n;++j)
        {
            pr=rr[j].a[i];
            for(int k=i;k<=m;++k)
                rr[j].a[k]-=(rr[i].a[k]*pr);
        }
    }
    for(int i=1;i<=m;i++)
    {
        flag=0;
        for(int j=1;j<=m;j++)
            flag|=(abs(rr[i].a[j])>=eps);
        if(flag)cnt++,ans+=rr[i].c;
    }
    printf("%d %d",cnt,ans);
    return 0;
}
 posted on 2022-11-06 19:30  hu_led  阅读(39)  评论(0编辑  收藏  举报