Atcoder Beginner Contest 236 E Average and Median
E. Average and Median
题目大意
给定一个数组\(x\),从中选出一些数字,要求俩俩相邻的数中必选一个,最大化平均值和中位数。
中位数的定义为第\(\lceil \frac{n}{2} \rceil\)小的数。
解题思路
刚开始想着各种贪心DP都觉得不对,直接整平均数不一定有最优子结构。事后发现可以二分答案判断。
就是二分平均数a
,如果对于选出来的数\(x_i\)有 \(\sum (x_i - a) \geq 0\),那么这个平均数就可以达到。
而判断能否选出这些数就是个简单dp,令\(g_i = x_i - a\),记\(f[i][0/1]\)表示第 \(i\)个数选或不选的最大值,\(f[i][0] = f[i - 1][1], f[i][1] = \min(f[i - 1][0], f[i - 1][1]) + g_i\),最后看\(\max(f[n][0], f[n][1])\)是否大于等于0即可。
中位数的话也一样,即如果有一半的数小于等于中位数。即令\(g_i = [x_i >= a] * 2 - 1\),同样用上述 \(f\),最后看 \(\max(f[n][0], f[n][1])\)是否大于0即可。
神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const double eps = 1e-5;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
vector<int> qwq;
LL sum = 0;
for(int i = 1; i <= n; ++ i){
LL x;
cin >> x;
qwq.push_back(x);
sum += x;
}
double l = 0, r = 1e9 + 7;
auto check = [qwq](double x){
vector<vector<double>> dp(qwq.size(), vector<double>(2, 0));
dp[0][1] = qwq[0] - x;
for(int i = 1; i < qwq.size(); ++ i){
dp[i][0] = dp[i - 1][1];
dp[i][1] = max(dp[i - 1][0], dp[i - 1][1]) + qwq[i] - x;
}
return dp.back()[0] >= 0 || dp.back()[1] >= 0;
};
while(l + eps < r){
double mid = (l + r) / 2;
if (check(mid))
l = mid;
else
r = mid;
}
cout << fixed << setprecision(8) << l << endl;
auto check2 = [qwq](int x){
vector<vector<int>> dp(qwq.size(), vector<int>(2, 0));
dp[0][1] = (qwq[0] >= x) * 2 - 1;
for(int i = 1; i < qwq.size(); ++ i){
dp[i][0] = dp[i - 1][1];
dp[i][1] = max(dp[i - 1][0], dp[i - 1][1]) + (qwq[i] >= x) * 2 - 1;
}
return dp.back()[0] > 0 || dp.back()[1] > 0;
};
int ll = 0, rr = 1e9 + 7;
while(ll + 1 < rr){
int mid = (ll + rr) >> 1;
if (check2(mid))
ll = mid;
else
rr = mid;
}
cout << ll << endl;
return 0;
}