#2108. 「JLOI2015」装备购买
题目描述
脸哥最近在玩一款神奇的游戏,这个游戏里有 nn 件装备,每件装备有 mm 个属性,用向量 \mathbf{z_i}=(a_1, \ldots ,a_j, \ldots , a_m)zi=(a1,…,aj,…,am) 表示 (1 \leq i \leq n, \ 1 \leq j \leq m1≤i≤n, 1≤j≤m),每个装备需要花费 c_ici,现在脸哥想买一些装备,但是脸哥很穷,所以总是盘算着怎样才能花尽量少的钱买尽量多的装备。对于脸哥来说,如果一件装备的属性能用购买的其他装备组合出(也就是说脸哥可以利用手上的这些装备组合出这件装备的效果),那么这件装备就没有买的必要了。
严格的定义是,如果脸哥买了 \mathbf{z_{i_1}}, \ldots , \mathbf{z_{i_p}}zi1,…,zip这 pp 件装备,那么对于任意待决定的 \mathbf{z_h}zh,不存在 b_1, \ldots ,b_pb1,…,bp使得 b_1\mathbf{z_{i_1}} + \ldots + b_p\mathbf{z_{i_p}} = \mathbf{z_h}b1zi1+…+bpzip=zh (b_ibi均是实数),那么脸哥就会买 \mathbf{z_h}zh,否则 \mathbf{z_h}zh 对脸哥就是无用的了,自然不必购买。
举个例子,\mathbf{z_1}=(1, 2, 3), \ \mathbf{z_2}=(3, 4, 5), \ \mathbf{z_h}=(2, 3, 4), \ b_1 =\frac{1}{2}, \ b_2 =\frac{1}{2}z1=(1,2,3), z2=(3,4,5), zh=(2,3,4), b1=21, b2=21,就有 b_1\mathbf{z_1} + b_2\mathbf{z_2} = \mathbf{z_h}b1z1+b2z2=zh ,那么如果脸哥买了 \mathbf{z_1}z1和 \mathbf{z_2}z2 就不会再买 \mathbf{z_h}zh了。
脸哥想要在买下最多数量的装备的情况下花最少的钱,你能帮他算一下吗?
输入格式
第一行两个数 n;m。接下来 n 行,每行 m 个数,其中第 i 行描述装备 i 的各项属性值。接下来一行 n 个数,其中 ci 表示购买第 i 件装备的花费。
输出格式
一行两个数,第一个数表示能够购买的最多装备数量,第二个数表示在购买最多数量的装备的情况下的最小花费
输入输出样例
输入 #1复制
3 3 1 2 3 3 4 5 2 3 4 1 1 2
输出 #1复制
2 2
说明/提示
如题目中描述,选择装备 1 装备 2,装备 1 装备 3,装备 2 装备 3 均可,但选择装备 1 和装备 2 的花费最小,为 2。
对于 100% 的数据, 1 <= n;m <= 500; 0 <= aj <= 1000。
题意:给你n个装备,每个装备有m个元素,每个装备还有价值
你要用最少的钱买最多的装备(并且买到的装备可以将其他没买到的装备组出来)
题解:
贪心先取最小的价值,线性基(用矩阵的高斯消元去写),涉及除法会掉精度,我们用逆元来做
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
struct node
{
ll b[505];
ll val;
}a[505];
bool cmp(const node &A,const node &B)
{
return A.val<B.val;
}
int vis[505];
ll quickpow(ll x,ll y)
{
ll ret=1;
while(y)
{
if(y%2) ret=ret*x%mod;
x=x*x%mod;
y/=2;
}
return ret;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%lld",&a[i].b[j]);
}
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i].val);
}
sort(a+1,a+n+1,cmp); //贪心从小到大价值
ll ans=0;
int tot=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i].b[j])
{
if(!vis[j])
{
tot++;
ans+=a[i].val;
vis[j]=i;
break;
}
else //高斯消元
{
ll t=a[i].b[j]*quickpow(a[vis[j]].b[j],mod-2)%mod; //本来是除,怕掉精度
for(int k=j;k<=m;k++)//此行之后所有数改变,前面为0,就不用改了
{
a[i].b[k]=(a[i].b[k]-t*a[vis[j]].b[k]%mod+mod)%mod;
}
}
}
}
}
printf("%d %lld\n",tot,ans);
return 0;
}