209. 装备购买

题目链接

209. 装备购买

脸哥最近在玩一款神奇的游戏,这个游戏里有 \(n\) 件装备,每件装备有 \(m\) 个属性,用向量 \(z[i]=(a_{i,1},a_{i,2} ,..,a_{i,m})\) 表示,每个装备需要花费 \(c_i\)

现在脸哥想买一些装备,但是脸哥很穷,所以总是盘算着怎样才能花尽量少的钱买尽量多的装备。

对于脸哥来说,如果一件装备的属性能用购买的其他装备组合出(也就是说脸哥可以利用手上的这些装备组合出这件装备的效果),那么这件装备就没有买的必要了。

严格的定义是,如果脸哥买了 \(z[i_1],z[i_2],…,z[i_p]\)\(p\) 件装备,并且不存在实数 \(b_1,b_2,…,b_p\) 使得 \(z[k]=b_1z[i_1]+b_2z[i_2]+…+b_pz[i_p]\),那么脸哥就会买 \(z[k]\),否则 \(z[k]\) 对脸哥就是无用的了,自然不必购买。

脸哥想要在买下最多数量的装备的情况下花最少的钱,你能帮他算一下吗?

输入格式

第一行包含两个整数 \(n\)\(m\)

接下来 \(n\) 行,每行 \(m\) 个数,其中第 \(i\) 行描述装备 \(i\) 的各项属性值。

接下来一行 \(n\) 个数,其中第 \(i\) 个数表示购买第 \(i\) 件装备的花费 \(c_i\)

输出格式

输出占一行,包含两个整数,第一个整数表示能够购买的最多装备数量,第二个整数表示在购买最多数量的装备的情况下的最小花费。

数据范围

\(1 \le n,m \le 500\),
\(0 \le a_{i,j} \le 1000\)

输入样例:

3 3
1 2 3
3 4 5
2 3 4
1 1 2

输出样例:

2 2

解题思路

线性基

显然题目要求找的线性基元素数量,即极大线性无关组的秩,可用高斯消元求解,关键在于求解的同时使代价最少
贪心策略:每次选择主元时选择满足要求的代价最少的那个
证明(来自蓝书):
该贪心策略可用反证法证明。假设花费价钱最少的基底是 \(z\left[i_{1}\right], z\left[i_{2}\right], \cdots, z\left[i_{p}\right]\), 其 中不包含价格最低的行向量 \(z[k]\) 。因为基底是极大线性无关子集, 所以 \(z[k]\) 能被 \(z\left[i_{1}\right], z\left[i_{2}\right], \cdots, z\left[i_{p}\right]\) 表出, 不妨设 \(z[k]=b_{1} z\left[i_{1}\right]+b_{2} z\left[i_{2}\right]+\cdots+b_{p} z\left[i_{p}\right]\)
移项变换可得 \(z\left[i_{p}\right]=\left(z[k]-b_{1} z\left[i_{1}\right]-\cdots-b_{p-1} z\left[i_{p-1}\right]\right) / b_{p}\), 即 \(z\left[i_{p}\right]\) 能被 \(z[k]\)\(z\left[i_{1}\right], z\left[i_{2}\right], \cdots, z\left[i_{p-1}\right]\) 表出。故 \(z[k], z\left[i_{1}\right], z\left[i_{2}\right], \cdots, z\left[i_{p-1}\right]\) 能与 \(z\left[i_{1}\right], z\left[i_{2}\right], \cdots, z\left[i_{p}\right]\) 表示相同的线性空间, 是一个总价格更低的基底, 与假设矛盾。

  • 时间复杂度:\(O(nm^2)\)

代码

// Problem: 装备购买
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/211/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=505;
const long double eps=1e-8;
int n,m,c[N],k,res;
long double a[N][N];
int main()
{
    help;
    cin>>n>>m;
    for(int i=0;i<n;i++)
    	for(int j=0;j<m;j++)cin>>a[i][j];
    for(int i=0;i<n;i++)cin>>c[i];
    for(int i=0;i<m;i++)
    {
    	int now=-1;
    	for(int j=k;j<n;j++)
    		if(fabs(a[j][i])>eps&&(now==-1||c[now]>c[j]))
    			now=j;
    	if(now==-1)continue;
    	res+=c[now];
    	swap(a[k],a[now]);
    	swap(c[k],c[now]);
    	for(int j=0;j<n;j++)
    	{
    		if(j!=k&&fabs(a[j][i])>eps)
    		{
    			long double rate=a[j][i]/a[k][i];
    			for(int t=0;t<m;t++)a[j][t]-=rate*a[k][t];
    		}
    	}
    	k++;
    	if(k==n)break;
    }
    cout<<k<<' '<<res;
    return 0;
}
posted @ 2022-07-29 23:13  zyy2001  阅读(34)  评论(0编辑  收藏  举报