NOIP模拟——卡牌游戏
题目
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; }