L最近喜欢上了一个卡片游戏,游戏规则是: 2个人一共拿2n张卡片,编号1…2n,每个人n张,然后进行n轮出牌,每轮2个人都打一张牌,,点数大的玩家每次获1分
L可以预测到对方要打牌的顺序。
同时,L有一次机会选择了某个时间点,从那个时候开始,每回合点数少者获胜。
请你帮助L获得最大的分数
输入
第一行是1个整数n
接下来n行表示,对手每次的出牌,根据这些数字,你一定知道了L手上的牌的吧
输出
1个整数,表示L能获得最高分数
样例输入
4
1
8
4
3
样例输出
3
标签
usaco2015dec
算是有难度的了吧,由于要随时删除元素(打出卡牌),所以就用set维护
数组f[i]表示前i个不选择时间时能拿的最大分数,g[i]表示后i个中选择时间点能拿的最大分数
则对于每一个a[i]我们选第一个比a[i]大的数,则f[i]=f[i-1]+1;对于每一个a[i]我们选第一个比a[i]小的数,则g[i]=g[i+1]+1;(如果有满足的数的话)
大佬说可以证明这样是最优的,可是蒟蒻并不会证
然后就只需要枚举断点
找出最大的f[i]+g[i+1];
#include<bits/stdc++.h>
using namespace std;
set<int> l,r;
bool flag[100005];
int a[50005],g[50005],f[50005],n;
int main(){
scanf("%d",&n);
int i;
memset(flag,true,sizeof(flag));
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
flag[a[i]]=false;
}
for(i=1;i<=2*n;i++)
{
if(flag[i])
{
r.insert(i);
l.insert(-i);
}
}
for( i=1;i<=n;i++)
{
set<int>::iterator it=r.upper_bound(a[i]);
if(it!=r.end())
{
r.erase(*it);
f[i]=f[i-1]+1;
}
else f[i]=f[i-1];
}
for(i=n;i>=1;i--)
{
set<int>::iterator it=l.upper_bound(-a[i]);
if(it!=l.end())
{
l.erase(*it);
g[i]=g[i+1]+1;
}
else g[i]=g[i+1];
}
int ans=0;
for( i=0;i<=n;i++)
{
ans=max(ans,f[i]+g[i+1]);
}
cout<<ans<<endl;
}