AtCoder Beginner Contest 236:E Average and Median

E Average and Median
题目具体题面见上述链接

题目大意

给出一个序列 a 1.. n a_{1..n} a1..n 长度为n,分别从中选择一些元素,使得平均数最大,以及中位数最大。对于每一个 i ( 1 ≤ i < n ) i (1\le i < n) i(1i<n) ,其中 a i − 1 , a i a_{i-1},a_i ai1,ai至少有一个要被选中。

题解

本题的步骤分成2步,如何使得平均数最大以及如何使得中位数最大。
这两个问题是完全独立的,互不干涉,所以本题我们可以看成是由两个问题组成。
第一个问题是,如何取数使得平均数最大?
如果我们直接使用动态规划求解平均数最大,很难设计一个正确的转移策略,原因是子状态的平均数会对接下来的平均数造成影响,不满足无后效性的特点。
因此,我们可以二分答案平均数来确定平均数最大的情况。
对于答案k,我们可以检验有没有取法,使得最后的平均数大于k。
如果有,就在二分的后半段区间内继续探索答案,否则就在前半段探索答案。
二分答案的检验方法是什么呢?
假如我们选了一些数 b 1.. m b_{1..m} b1..m ,这些数的平均数如果比k大,就满足 Σ ( b i − k ) \Sigma(b_i-k) Σ(bik) >=0。
根据上述,我们在 a 1.. n a_{1..n} a1..n 中选择一些数,满足取的每一个数与k作差后的和最大。然后将最大值与0比较即可。
对于这个求最大的过程,我们就可以使用动态规划从前往后进行转移了。
f[1][ i ],表示从第一个位置到当前位置 i,并且取 a i a_i ai Σ ( b j − k ) \Sigma(b_j-k) Σ(bjk)( b j b_j bj 是位于位置i之前的数) 的最大值,f[0][i]则表示不取 a i a_i ai的最大值。
转移方程为

f[1][i] = max(f[1][i - 1] + a[i] - k,f[0][i - 1] + a[i] - k)
f[0][i] = f[1][i - 1]
如果a[i] 不取,那么a[i - 1]只能取

完成了第一个问题,接下来第二个问题,如何取使得中位数最大?
有了平均数的解法,我们就很容易想到中位数其实也可以用二分答案探测来求解。
对于答案k,如果能有取法,使得中位数比k大,那么这些数中一定满足至少一半的数比k大。
那么,我们要尽量少取比k小的数,尽量多取比k大的数。
在 动态规划的过程中,对于取到比k小的数,看错 -1 贡献,比k大的数,看成 +1 贡献。最后的贡献结果如果正的,就说明至少取的数中有一半比k大,中位数比k大。
转移方程为

now = a[i]>k?1:-1;
f[1][i]=max(f[1][i - 1]+now,f[0][i - 1]+now)
f[0][i]=f[1][i-1]
如果a[i] 不取,那么a[i - 1]只能取

我的代码

你可能感兴趣的:(ACM,动态规划,算法,acm竞赛)