Average and Median(二分、状态机DP)

题意

给定一个长度为\(n\)的序列,你要从中选择一些数字。要求相邻两个数字必须至少选择一个。求:

  • 选择出来的数字平均值最大可能是多少
  • 选择出来的数字中位数最大可能是多少

数据范围

\(2 \leq n \leq 100000\)

思路

考虑二分查找答案。

  • 对于平均值,假设当前查找的答案是\(K\),那么对序列的每个元素减去\(K\)。然后问题转化为是否能从序列中选择一些数字,使得这些数字之和是否非负。
  • 对于中位数,假设当前查找的答案是\(K\),那么对序列的每个元素,如果元素大于等于\(K\),那么设为\(1\);如果小于\(K\),那么设为\(-1\)。然后问题转化为是否能从序列中选择一些数字,使得这些数字之和大于\(0\)

对于以上两个问题,其实就是从序列中选出一些数字,这些数字之和的最大值可能是多少。对于这个问题,考虑DP。设\(f_{i, 0}\)表示从前\(i\)个元素中选,且第\(i\)个元素必须选,所选元素和的最大值;\(f_{i, 1}\)表示从前\(i\)个元素中选,且第\(i\)个元素不选,所选元素和的最大值。那么转移方程就是:\(f_{i, 0} = \max\{f_{i - 1, 0}, f_{i - 1, 1}\}\)\(f_{i, 1} = f_{i - 1,1}\)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;

const int N = 100010;
const double eps = 1e-6;

int n;
ll a[N];
double b[N];
int c[N];
double f[N][2];
int g[N][2];

bool check1(double x)
{
    for(int i = 1; i <= n; i ++) b[i] = a[i] - x;
    for(int i = 1; i <= n; i ++) {
        for(int j = 0; j < 2; j ++) {
            f[i][j] = 0;
        }
    }
    f[1][0] = b[1];
    for(int i = 1; i <= n; i ++) {
        f[i][0] = max(f[i - 1][0], f[i - 1][1]) + b[i];
        f[i][1] = f[i - 1][0];
    }
    return max(f[n][0], f[n][1]) >= eps;
}

bool check2(int x)
{
    for(int i = 1; i <= n; i ++) {
        if(a[i] < x) c[i] = -1;
        else c[i] = 1;
    }
    for(int i = 1; i <= n; i ++) {
        for(int j = 0; j < 2; j ++) {
            g[i][j] = 0;
        }
    }
    g[1][0] = c[1];
    for(int i = 1; i <= n; i ++) {
        g[i][0] = max(g[i - 1][0], g[i - 1][1]) + c[i];
        g[i][1] = g[i - 1][0];
    }
    return max(g[n][0], g[n][1]) > 0;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
    double l = 1, r = 1e9;
    while(r - l > eps) {
        double mid = (l + r) / 2;
        if(check1(mid)) l = mid;
        else r = mid;
    }
    printf("%.6f\n", r);
    int l2 = 1, rr = 1e9;
    while(l2 < rr) {
        int mid = l2 + rr + 1 >> 1;
        if(check2(mid)) l2 = mid;
        else rr = mid - 1;
    }
    printf("%d\n", rr);
    return 0;
}
posted @ 2022-03-16 15:08  pbc的成长之路  阅读(67)  评论(0编辑  收藏  举报