CF794E Choosing Carrot

题解:

我们可以先考虑不进行 \(k\) 操作的情况

​ 当取完的时候,因为最优(B都可以通过他的 \(\dfrac{n}2-1\) 次机会取走,所以只有 \(mid\)\(mid+1\) 对于B来说是无能为力的),肯定是剩下中间的几个

​ 然后由A选择小的那个,剩下大的,则有两种情况:

\[ans=\begin{cases}max(a[mid],a[mid+1])~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~n为偶数\\max(min(a[mid],a[mid-1]),min(a[mid],a[mid+1]))~~~n为奇数\end{cases} \]

然后考虑进行 \(k\) 操作

​ 当A多吃,只有 \(mid\) 会被影响,发生变化,而每个 \(i\in [1,k-2]\) 是由 \(i+2\) 转移过来的,三种可能:

要么左右各拿一个 \(mid\) 不变,要么左边或者右边拿俩 \(mid\) 变化,而我们考虑奇偶即可,左右拿俩可以合并到一个数组,但是如果要求左右拿俩的情况,则需要用到动态规划

​ 设 \(h[i]\) 为偶数个数当 \(i\) 为中点时最大值, \(g[i]\) 为奇数个数当 \(i\) 为中点时最大值,可得方程:

\[h[min(i,n-i)]=max(h[min(i,n-i)],max(a[i],a[i+1]))\\ g[min(i-1,n-i)]=max(g[min(i-1,n-i)],max(min(a[i],a[i-1]),min(a[i],a[i+1]))) \]

​ 因为可以类比,所以这里的转移用到了上面不进行 \(k\) 操作时的转移, \(i/i-1\)\(n-i\) 代表左拿还是右拿。

所以,设 \(ans[i]\) 为剩下 \(i\) 个元素能取到的最大值,转移方程为:

\[ans[2*i]=max(ans[2*i+2],h[i])\\ ans[2*i+1]=max(ans[2*i+3],g[i]) \]

最后倒序输出,时间复杂度 \(O(n)\)

代码

#include <cstdio>
#include <iostream>

using namespace std;
const int MAXN=300005;
int n,a[MAXN],h[MAXN],g[MAXN],ans[MAXN];

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        ans[1]=max(ans[1],a[i]);
    }
    for(int i=1;i<n;i++) h[min(i,n-i)]=max(h[min(i,n-i)],max(a[i],a[i+1]));//代表偶数下的最值 
    for(int i=2;i<n;i++) g[min(i-1,n-i)]=max(g[min(i-1,n-i)],max(min(a[i-1],a[i]),min(a[i],a[i+1])));//代表奇数下的最值 
    for(int i=n/2;i>=1;i--)
    {
        ans[i<<1]=max(ans[(i+1)<<1],h[i]);
        ans[i<<1|1]=max(ans[(i+1)<<1|1],g[i]);
    }
    for(int i=n;i>=1;i--)
        printf("%d ",ans[i]);
}
posted @ 2020-08-02 16:10  jasony_sam  阅读(120)  评论(0编辑  收藏  举报