3 条题解
-
0
题目大意
有 个烤箱,每个烤箱 每秒会产出 个蛋糕。Maple 每秒只能从一个烤箱收集一次蛋糕。如果她在某一秒收集烤箱 ,则只能获得自上一次收集该烤箱以来累积的蛋糕。
题目要求在 秒内(或最后 秒策略内)最大化收集的蛋糕总数。
题解思路(逆向思维)
这是一个典型的排序不等式与贪心策略问题。
1. 关键性质分析 根据题意,一个烤箱 产出的蛋糕数量取决于 Maple 上一次访问它的时间间隔。如果我们考虑时间从后往前倒推,我们可以自由安排最后 秒去访问哪些烤箱。
假设我们确定了最后 秒(由于只有 个烤箱,有效操作次数不超过 次)访问烤箱的顺序为 :
- 在倒数第 秒(即第 秒),访问烤箱 。假设之前没来过,它积累了 秒的蛋糕,产出为 。但根据代码逻辑
max(0, t - i),我们更严谨地从数学归纳看: - 在倒数第 秒(第 秒),访问烤箱 ,产出为 。
- 以此类推,对于倒数第 秒访问的烤箱 ,其产出乘数因子为 。
2. 贪心策略的数学证明 我们希望最大化总蛋糕数:
$$\text{Total} = \sum_{i=1}^{n} a_{p_i} \cdot \max(0, t - i + 1) $$根据排序不等式(Rearrangement Inequality):
逆序和 乱序和 顺序和。
这里乘数因子 是随着 增大而递减的序列(或者尾部为 )。为了让乘积之和最大,我们应该将较大的 分配给较大的乘数因子。
因此,最优策略是:
- 将数组 降序排列。
- 最大的 对应乘数 。
- 第二大的 对应乘数 。
- ...
- 当 时,乘数为 ,表示剩余时间不足以让该烤箱产出一块蛋糕(或者说没有机会再去收取了)。
3. 最终公式 设排序后的序列仍记为 ,答案计算公式为:
$$\text{Ans} = \sum_{i=1}^{n} \left( a_i \cdot \max(0, t - i + 1) \right) $$复杂度分析
- 时间复杂度:排序占用 ,求和遍历 。总复杂度为 。
- 空间复杂度:存储数组 需要 额外空间。
标准代码(C++)
#include <bits/stdc++.h> using namespace std; typedef long long ll; void solve(){ int n, t; cin >> n >> t; ll ans = 0; vector<int> a(n); for(int i = 0; i < n; i++) cin >> a[i]; // 降序排序:将产蛋糕多的烤箱放在前面(对应更大的时间乘数) sort(a.begin(), a.end(), greater<int>()); for(int i = 0; i < n; i++) { // 乘数为 max(0, t - i) ans += 1ll * a[i] * max(0, t - i); } cout << ans << '\n'; } int main(){ ios::sync_with_stdio(false); cin.tie(0); int TC; cin >> TC; while(TC--){ solve(); } return 0; } - 在倒数第 秒(即第 秒),访问烤箱 。假设之前没来过,它积累了 秒的蛋糕,产出为 。但根据代码逻辑
-
0
问题重述
我们有 个烤炉,第 个烤炉每秒生产 个蛋糕。
在 秒内的每一秒结束时,我们可以传送到任意一个烤炉(可以重复访问同一个烤炉),并取走该烤炉中当前积累的所有蛋糕。
问: 秒内最多能收集到多少蛋糕?
关键观察
-
每个烤炉的贡献只取决于最后一次访问的时间
因为如果我们在某个烤炉最后一次访问之后还去访问它,那么它积累的蛋糕会被取走,但后续它还会继续生产,所以最后一次访问决定了该烤炉被取走的总量。 -
最优策略中,最后 秒应该访问不同的烤炉
因为如果我们重复访问同一个烤炉,就浪费了一次访问机会(这个访问可以用于另一个烤炉,从而获得更多蛋糕)。
因此,在最优方案中,最后 秒,每一秒访问一个不同的烤炉。 -
反向思考
设我们安排一个访问顺序 (其中 ),表示:- 在第 秒结束时访问烤炉 (最后一次访问)
- 在第 秒结束时访问烤炉
- ……
- 在第 秒结束时访问烤炉
对于顺序中的第 个烤炉 ,它从上次被清空(或从第 1 秒开始)到被访问的时间长度为: [ \max(0, m - i + 1) ] 因为:
- 如果 ,它在第 秒结束时被访问,所以积累了 秒的蛋糕。
- 如果 ,则它从未被访问,贡献为 。
因此该烤炉贡献的蛋糕数为: [ a_{p_i} \cdot \max(0, m - i + 1) ]
-
贪心分配
为了最大化总和,应该让生产速度 大的烤炉获得更大的时间乘数 。
由于乘数随 增大而减小( 时乘数最大, 时乘数最小),我们应该将 从大到小排序,然后依次分配给 。
算法步骤
- 读入 和数组 。
- 将 按非递增排序。
- 初始化答案 。
- 对于 到 (对应顺序中的第 个烤炉): [ \text{ans} \ += a[i] \cdot \max(0, m - i) ] 注意这里 从 0 开始,所以乘数为 。
- 输出 。
数学表达
设排序后的数组为 ,则答案为: [ \text{ans} = \sum_{i=1}^{n} a_{(i)} \cdot \max(0, m - i + 1) ] 或等价地: [ \text{ans} = \sum_{i=1}^{\min(m, n)} a_{(i)} \cdot (m - i + 1) ]
示例验证
示例 1
排序后:
计算:- :
- :
- :
总和:,与样例一致。
示例 2
排序后:- :
- :
- :
总和:,与样例一致。
示例 3
排序后:- :
总和:,与样例一致。
复杂度分析
- 排序:
- 求和:
- 总复杂度:,满足 总和 的要求。
代码实现(C++)
#include <bits/stdc++.h> using namespace std; typedef long long ll; void solve() { int n, m; cin >> n >> m; vector<int> a(n); for (int i = 0; i < n; i++) cin >> a[i]; sort(a.begin(), a.end(), greater<int>()); ll ans = 0; for (int i = 0; i < n; i++) { ans += 1ll * a[i] * max(0, m - i); } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); int t; cin >> t; while (t--) solve(); return 0; } -
-
0
题目详解
问题重述
我们有 个烤炉,第 个烤炉每秒生产 个蛋糕。
在 秒内的每一秒结束时,我们可以传送到任意一个烤炉(可以重复访问同一个烤炉),并取走该烤炉中当前积累的所有蛋糕。
问: 秒内最多能收集到多少蛋糕?
关键观察
-
每个烤炉的贡献只取决于最后一次访问的时间
因为如果我们在某个烤炉最后一次访问之后还去访问它,那么它积累的蛋糕会被取走,但后续它还会继续生产,所以最后一次访问决定了该烤炉被取走的总量。 -
最优策略中,最后 秒应该访问不同的烤炉
因为如果我们重复访问同一个烤炉,就浪费了一次访问机会(这个访问可以用于另一个烤炉,从而获得更多蛋糕)。
因此,在最优方案中,最后 秒,每一秒访问一个不同的烤炉。 -
反向思考
设我们安排一个访问顺序 (其中 ),表示:- 在第 秒结束时访问烤炉 (最后一次访问)
- 在第 秒结束时访问烤炉
- ……
- 在第 秒结束时访问烤炉
对于顺序中的第 个烤炉 ,它从上次被清空(或从第 1 秒开始)到被访问的时间长度为: [ \max(0, m - i + 1) ] 因为:
- 如果 ,它在第 秒结束时被访问,所以积累了 秒的蛋糕。
- 如果 ,则它从未被访问,贡献为 0。
因此该烤炉贡献的蛋糕数为: [ a_{p_i} \cdot \max(0, m - i + 1) ]
-
贪心分配
为了最大化总和,应该让生产速度 大的烤炉获得更大的时间乘数 。
由于乘数随 增大而减小( 时乘数最大, 时乘数最小),我们应该将 从大到小排序,然后依次分配给 。
算法步骤
- 读入 和数组 。
- 将 按非递增排序。
- 初始化答案 。
- 对于 到 (对应顺序中的第 个烤炉): [ \text{ans} \ += a[i] \cdot \max(0, m - i) ] 注意这里 从 0 开始,所以乘数为 。
- 输出 。
数学表达
设排序后的数组为 ,则答案为: [ \text{ans} = \sum_{i=1}^{n} a_{(i)} \cdot \max(0, m - i + 1) ] 或等价地: [ \text{ans} = \sum_{i=1}^{\min(m, n)} a_{(i)} \cdot (m - i + 1) ]
示例验证
示例 1
排序后:
计算:- :
- :
- :
总和:,与样例一致。
示例 2
排序后:- :
- :
- :
总和:,与样例一致。
示例 3
排序后:- :
总和:,与样例一致。
复杂度分析
- 排序:
- 求和:
- 总复杂度:,满足 总和 的要求。
代码实现(C++)
#include <bits/stdc++.h> using namespace std; typedef long long ll; void solve() { int n, m; cin >> n >> m; vector<int> a(n); for (int i = 0; i < n; i++) cin >> a[i]; sort(a.begin(), a.end(), greater<int>()); ll ans = 0; for (int i = 0; i < n; i++) { ans += 1ll * a[i] * max(0, m - i); } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); int t; cin >> t; while (t--) solve(); return 0; } -
- 1
信息
- ID
- 6348
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者