原题链接acwing 训练士兵
题目描述
在蓝桥王国中,有 n 名士兵,这些士兵需要接受一系列特殊的训练,以提升他们的战斗技能。
对于第 i名士兵来说,进行一次训练所需的成本为 pi枚金币,而要想成为顶尖战士,他至少需要进行 ci次训练。
为了确保训练的高效性,王国推出了一种组团训练的方案。该方案包含每位士兵所需的一次训练,且总共只需支付 S枚金币(组团训练方案可以多次购买,即士兵可以进行多次组团训练)。作为训练指挥官,请你计算出最少需要花费多少金币,才能使得所有的士兵都成为顶尖战士?
输入格式
第一行包含两个整数 n和 S,表示士兵的数量和进行一次组团训练所需的金币数。
接下来的 n行,每行包含两个整数 pi和 ci,表示第 i 名士兵进行一次训练的金币成本和要成为顶尖战士所需的训练次数。
输出格式
输出一个整数,表示使所有士兵成为顶尖战士所需的最少金币数。
数据范围
对于 40%的测评用例,1≤n≤103,1≤pi,ci≤105,1≤S≤107。
对于所有测评用例,1≤n≤105,1≤pi,ci≤106,1≤S≤1010。(在acwing测评中,ci的数据范围<=107)
输入样例:
3 6
5 2
2 4
3 2
输出样例:
16
样例解释
花费金币最少的训练方式为:进行 2次组团训练,花费 2×6=12枚金币,此时士兵 1,3已成为顶尖战士;再花费 4枚金币,让士兵 2进行两次训练,成为顶尖战士。总花费为 12+4=16。
解题思路
要想将所有士兵升级为满级,要么组团训练,要么一个一个单独训练,为了使花费的金币最少,那么只需要在每次升级的时候比较组团训练的费用和一个一个单独训练的费用即可,谁便宜选谁。那么可以设每次所有没有满级的士兵单独训练的花费总和为sum_cost,那么sum_cost的初始值为所有士兵每次升级的花费之和(所有士兵一开始不是满级)。然后我们将最大的训练次数保存下来,用max_t来表示。
m
a
x
_
t
=
m
a
x
(
c
1
,
c
2
,
c
3
,
.
.
.
.
.
,
c
n
)
max\_t=max(c1,c2,c3,.....,cn)
max_t=max(c1,c2,c3,.....,cn)
最后再遍历训练次数for(int i=1;i<=max_t;i++),然后比较是组团训练更便宜还是单独训练更便宜,最后的答案ans+=便宜的训练。但是遍历升级次数的过程,会有士兵已经升级到满级了,所以sum_cost是变化的。比如上述样例,当进行两次组团训练后,第一个士兵和第3个士兵就满级了,无须继续训练,这时所有未满级的士兵一个一个单独训练升一级的花费为2,小于组团训练花费的6,所以这个时候应该让未满级的士兵单独训练。因此我们可以设定一个哈希表a[N],a[i]=k,表示所有需要i次就能满级的士兵单次升级花费之和为k,如上述样例中第一个士兵和第三个士兵需要升级两次就能满级的士兵单次升级花费之和为5+3=8,即a[2]=8。那么在进行两次组团训练之后,这两个士兵满级,于是sum_cost=sum_cost-a[2]。这样就能更新sum_cost了。然后继续比较组团训练和单独训练哪个花费更少,每次选择更少的即可。
代码部分
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int p[N],c[N];
long long a[100*N]; //a[i]=k表示所有需要升级i次才满级的所有士兵升一级的总费用为k,这里样例中ci超过了10的6次方
//此题样例int a[100*N]也能过,我开long long是为了防止极端数据所有士兵每次升级都需要10的6次方,所有士兵都需要升级10的6次方次
int main()
{
int n;
long long s;
cin >> n >> s;
int max_t = 0; //记录所有士兵中最大的单独训练次数
long long ans = 0; //最终消耗金币的数量
long long sum_cost = 0; //记录所有没有升级到满级的士兵一个一个升一级的总花销
for(int i = 1;i<=n;i++)
{
cin>>p[i]>>c[i];
sum_cost+=p[i]; //初始值就是所有士兵每次升级的花费总和
a[c[i]]+=p[i]; //记录所有需要升c[i]级的士兵每次升级的花费总和
max_t = max(max_t,c[i]);
}
for(int i = 1;i<=max_t;i++) //遍历每一次升级,判断是选择组团好还是单独训练好
{
if(sum_cost>s)
{
ans+=s; //团体训练更加划算
}
else
{
ans+=sum_cost; //士兵单独训练更划算
}
//更新单独训练的花费,已经满级的士兵无须在单独训练
sum_cost-=a[i]; //因为所有士兵已经升级了i级,那么所以i级为满级的士兵无须继续升级,下一次升级的花费无须计算这些士兵
}
cout<<ans<<endl;
return 0;
}
这样时间复杂度就为O(n)。