# P10387 [蓝桥杯 2024 省 A] 训练士兵
## 题目描述
在蓝桥王国中,有 n 名士兵,这些士兵需要接受一系列特殊的训练,以提升他们的战斗技能。对于第 i名士兵来说,进行一次训练所需的成本为 p_i 枚金币,而要想成为顶尖战士,他至少需要进行 c_i 次训练。
为了确保训练的高效性,王国推出了一种组团训练的方案。该方案包含每位士兵所需的一次训练,且总共只需支付S 枚金币(组团训练方案可以多次购买,即士兵可以进行多次组团训练)。
作为训练指挥官,请你计算出最少需要花费多少金币,才能使得所有的士兵都成为顶尖战士?## 输入格式
输入的第一行包含两个整数n和S,用一个空格分隔,表示士兵的数量和进行一次组团训练所需的金币数。
接下来的n行,每行包含两个整数p_i 和c_i,用一个空格分隔,表示第i名士兵进行一次训练的金币成本和要成为顶尖战士所需的训练次数。## 输出格式
输出一行包含一个整数,表示使所有士兵成为顶尖战士所需的最少金币数。
## 输入输出样例 #1
### 输入 #1
```
3 6
5 2
2 4
3 2
```### 输出 #1
```
16
```## 说明/提示
花费金币最少的训练方式为:进行2次组团训练,花费2 × 6 = 12$ 枚金币,此时士兵1, 3 已成为顶尖战士;再花费 4枚金币,让士兵2进行两次训练,成为顶尖战士。总花费为12 + 4 = 16。
对于40\%的评测用例,1 ≤ n ≤ 10^3,1 ≤ p_i
, c_i ≤ 10^5,1 ≤ S ≤ 10^7。对于所有评测用例,1 ≤ n ≤ 10^5,1 ≤ p_i
, c_i ≤ 10^6,1 ≤ S ≤ 10^{10}。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll p[10000000+10], c[10000000+10],cnt[10000000+10];
int main()
{
ll n,s;
cin>>n>>s;
ll sum=0,now=0,ans=0;
for(int i=0;i<n;i++)
{
cin>>p[i]>>c[i];
now+=p[i]; //每个士兵单独训练一次所需的费用
sum+=c[i]*p[i];//所有士兵单独训练完所需费用
cnt[c[i]]+=p[i]; //训练成c[i]次的人一次训练所需的花费
}
for(int i=1;i<=1000005;i++) //考虑ci的范围小于1e6 遍历每个士兵
{
if(now>s) //单独训练士兵大于组团训练
{
ans+=s; 更新当前已经训练士兵的花费
sum-=now; 更新剩余士兵单独训练所需费用
now-=cnt[i]; 更新剩余士兵单独训练一次所需费用
}else{
break;
}
}
cout<<ans+sum;
return 0;
}
总结思考:
题目本身不难,在写的时候注意数据的范围要开longlong,数组命名要规范,做的时候犯了一个低级错误,写成count命名数组;
对样例进行模拟,now=8,sum=24,
cnt[ c[0] ]=cnt[2]=5,
cnt[ c[1] ]=cnt[4]=2,
cnt[ c[2] ]=cnt[2]=5+3=8;
遍历每个士兵,从轮数1开始遍历,如果单独训练的所有士兵一次的花费要大于组团训练,选择组团训练,开始更新ans记录当前的花费,ans=0+s=6,sum=24-8=12,now=8-0=8;
第二轮:ans=6+6=12,sum=12-8=4,now=8-8=0;
这个时候开始跳出循环,ans=12+4=16;
题目的精妙在于,使用cnt数组来记录训练c[i]次士兵一次训练所需的花费,在更新数据的时候,当有的士兵已经结束训练次数结束的时候,要更新现在剩余所有士兵训练一次所需的花费,即now值