思路
理论
反悔贪心。
和它的弱化版相比,这道题的数据范围明显就不能动态规划,但是好在,这一题变成了通过 c i c_i ci 英镑购买 1 1 1 个单位的幸福。很明显就可以进行贪心。
但这一道题直接用贪心存在一个问题,就是金钱总数并不是固定的,而是每个月一点一点发,存在一定的后效性。这时我们可以考虑反悔贪心。
反悔贪心的过程是这样的:和普通的贪心一样,我们在每一个月份依然能购买幸福就购买幸福。但是当我们没有钱购买时,我们可以从之前购买幸福每一次花费的英镑数中取一个最大值,如果最大值比我们此刻购买需要花费的英镑数多,则我们可以实现“退货”,把我们的英镑余额加上最大值。这时就可以购买本月的幸福了。
注意:在“退货”的过程中,我们先将幸福值减去 1 1 1,随后又加上 1 1 1,因此幸福值总数不变,但是余额增加了。当我们不进行“退货”直接购买时,幸福值总数才会加 1 1 1。
实现
这道题具体实现起来是这样的:
首先我们开一个大根堆,用于对每一次花费自动取最大值:
priority_queue<int>q;
接下来输入部分,然后按照理论来做。
当我们的余额可以直接购买幸福时:
if (sum >= c[i]) {
sum -= c[i];
q.push(c[i]);
ans++;
}
当我们要执行退货时:
else if (!q.empty()) {
if (q.top() > c[i]) {
sum += q.top();
q.pop();
sum -= c[i];
q.push(c[i]);
}
}
这道题就完成了!
完整代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 2e5 + 10;
int c[MAXN];
void solve()
{
priority_queue<int>q;
int m, x, sum = 0;
int ans = 0;
cin >> m >> x;
for (int i = 1; i <= m; i++) cin >> c[i];
for (int i = 1; i <= m; i++) {
if (sum >= c[i]) {
sum -= c[i];
q.push(c[i]);
ans++;
}
else if (!q.empty()) {
if (q.top() > c[i]) {
sum += q.top();
q.pop();
sum -= c[i];
q.push(c[i]);
}
}
sum += x;
}
cout << ans<<endl;
return;
}
signed main()
{
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}