卡牌游戏
【题意】
L最近喜欢上了一个卡片游戏,游戏规则是:
2 个人一共拿 2n 张卡片,编号 1..2n,每个人 n 张,然后进行 n 轮出牌,每轮 2 个
人都打一张牌,,点数大的玩家每次获1分。 L可以预测到对方要打牌的顺序。
同时,L有一次机会选择了某个时间点,从那个时候开始,每回合点数少者获胜。
请你帮助 L获得最大的分数
【输入】
第一行是1个整数n,n是偶数
接下来n行表示,对手每次的出牌,根据这些数字,你一定知道了 L手上的牌的吧
【输出】
1个整数,表示L能获得最高分数
Sample Input
4
1
8
4
3
Sample Output
3
【子任务】
30%数据,N<=100
100%数据,N<=50000
分析
很明显,要分数最大,我们在分段点之前(也就是以点数大为胜的那一边)每次应该尽量选择比当前这个点数大一点点的那一个,在分段点之后(也就是以点数小为胜的那一边)应该尽量选择比当前点小一点点的那一个,这样就可以保证最优了
然后我们正着搞一遍,反着搞一遍,最后O(n)枚举一下,就可以了
具体的实现:咱用set,加upper_bound
存疑:正着搞和反着搞L不会出同一张牌吗?
解释:详见评论区(感谢各位大佬的出手相救,小生感激不尽%%%%)
代码
#include<bits/stdc++.h>
#define in read()
#define N 100009
#define ll long long
using namespace std;
inline int read(){
char ch;int res=0;
while((ch=getchar())<'0'||ch>'9');
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return res;
}
int n,a[N],f[N],g[N];
bool vis[N];
set<int > l,r;//l-->from left to right,r-->from right to left
int main(){
n=in;
int i,j,k;
for(i=1;i<=n;++i){
a[i]=in;
vis[a[i]]=1;
}
for(i=1;i<=2*n;++i){
if(!vis[i]){
l.insert(i);
r.insert(-i);//we will find the biggeset
}
}
for(i=1;i<=n;++i){
set<int >:: iterator it=l.upper_bound(a[i]);
if(it!=l.end()){
l.erase(*it);
f[i]=f[i-1]+1;
}
else f[i]=f[i-1];
}
for(i=n;i>=1;--i){
set<int >:: iterator it=r.upper_bound(-a[i]);
if(it!=r.end()){
r.erase(*it);
g[i]=g[i+1]+1;
}
else g[i]=g[i+1];
}
int ans=-1;
for(i=0;i<=n;++i)
ans=max(ans,f[i]+g[i+1]);
printf("%d",ans);
return 0;
}