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]);
}