Powerful Discount Tickets(贪心,数学)
题意
有\(N\)件物品,每件物品价格为\(A_i\)元。
你现在有\(K\)张优惠券。对于一个价格为\(X\)的物品,如果你使用\(y\)张优惠券,则你需要花费\(\lfloor \frac{X}{2^y} \rfloor\)元。
求购买所有物品需要花费多少元钱?
题目链接:https://atcoder.jp/contests/abc141/tasks/abc141_d
数据范围
\(1 \leq N, K \leq 10^5\)
思路
我们可以观察一下\(\lfloor \frac{X}{2^0} \rfloor\),\(\lfloor \frac{X}{2^1} \rfloor\),\(\lfloor \frac{X}{2^2} \rfloor\),\(\dots\)的结果。我们可以发现变化幅度是减小的,也就是将优惠券用在价格更高的物品上更好。
因此,我们可以考虑每次使用一张优惠券,用在当前价格最高的物品上,然后该物品价格除以\(2\)。这个过程可以使用优先队列进行维护。
这个做法为什么是正确的呢?因为这里有一条性质:对于任意正整数\(X, b_1, b_2\),有\(\lfloor \frac{\lfloor \frac{X}{b_1} \rfloor}{b_2} \rfloor = \lfloor \frac{X}{b_1 b_2} \rfloor\)。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
int n, k;
int main()
{
priority_queue<int> que;
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i ++) {
int x;
scanf("%d", &x);
que.push(x);
}
while(k --) {
int t = que.top();
que.pop();
que.push(t / 2);
}
ll ans = 0;
while(que.size()) {
int t = que.top();
ans += (ll)t;
que.pop();
}
printf("%lld\n", ans);
return 0;
}