题目大意
有两种活动 a , b a,b a,b,数量分别是 n , m ( 1 ≤ n , m ≤ 1 e 5 ) n,m(1 \leq n,m \leq 1e5) n,m(1≤n,m≤1e5),每天只能在上午或者下午选择一种活动进行,每种活动可以重复选择但被选择一次后权值就会变为原来的 60 % 60\% 60%,特别地 b b b类活动在下午被选择时只能得到其权值的 80 % 80\% 80%。现在有 T T T天,其中限制最少选择 k ( 1 ≤ k ≤ T ≤ 1 e 5 ) k(1 \leq k \leq T \leq 1e5) k(1≤k≤T≤1e5)天下午,问最终能得到的最大权值和。
解题思路
一开始分析题目发现比较复杂,容易想乱,但是实际上一步步地简化拆解问题,从一般到特殊地去寻求解决问题的方法,往往能迎刃而解。
首先不难得出下午的天数只需要恰好 k k k天。然后分析得出,两种活动每次选择时下次都会变为原来的 0.6 0.6 0.6,但是 b b b活动在下午被选择时会乘上 0.8 0.8 0.8。假设 b b b活动下午不需要乘 0.8 0.8 0.8,实际上就是所有的权值丢进优先队列,每次拿出队首加上,然后将队首乘上 0.6 0.6 0.6丢进去,就这么简单。在目前分析的基础上考虑 b b b活动下午乘 0.8 0.8 0.8,既然 a a a活动在下午没有影响, b b b活动在上午没有影响,那么可以这样想:队首如果是 a a a活动那么优先考虑下午,如果是 b b b活动优先选择上午,这样贪心能保证前面的每步都会加上更大的值。但这时出现了一个问题,如果上午的分配完了呢?这时有可能 b b b活动乘上 0.8 0.8 0.8会更大,那么就需要比较权值最大 b b b活动乘上比例、权值最大 a a a活动选出最大的。为了维护这个过程,使用两个优先队列更为方便。
PS:一开始我多想了一点,如果上午下午都有名额,若 b b b活动最大的乘上 0.8 0.8 0.8比 a a a活动更大,能不能先选 b b b?实际上不能,因为一旦选过之后就会导致其乘上 0.6 0.6 0.6,可以假设乘上 0.6 0.6 0.6相对于 a a a活动最大的大小关系,经过分析发现这样会导致答案变小
#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const int Mod = 1e9 + 7;
const int maxn = 1e5 + 10;
priority_queue<double> q1, q2;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m, t, k;
cin >> n >> m >> t >> k;
for (int i = 1, x; i <= n; i++) {
scanf("%d", &x);
q1.push(x);
}
for (int i = 1, x; i <= m; i++) {
scanf("%d", &x);
q2.push(x);
}
int mo = t - k, af = k;
double ans = 0;
while (mo || af) {
double a = q1.top();
double b = q2.top();
if (a > b) {
ans += a;
q1.pop();
q1.push(a * 0.6);
if (af)
af--;
else if (mo)
mo--;
} else {
if (mo) {
ans += b;
q2.pop();
q2.push(b * 0.6);
mo--;
} else if (af) {
if (b * 0.8 >= a) {
ans += b * 0.8;
q2.pop();
q2.push(b * 0.6);
} else {
ans += a;
q1.pop();
q1.push(a * 0.6);
}
af--;
}
}
}
printf("%.2lf\n", ans);
return 0;
}