题目(卡牌游戏):
【题目描述】
L最近喜欢上了一个卡片游戏,游戏规则是: 2个人一共拿2n张卡片,编号1..2n,每个人n张,然后进行n轮出牌,每轮2个人都打一张牌,,点数大的玩家每次获1分
L可以预测到对方要打牌的顺序。
同时,L有一次机会选择了某个时间点,从那个时候开始,每回合点数少者获胜。
请你帮助L获得最大的分数
【输入】
第一行是1个整数n
接下来n行表示,对手每次的出牌,根据这些数字,你一定知道了L手上的牌的吧
【输出】
1个整数,表示L能获得最高分数
【样例输入】
4
1
8
4
3
【样例输出】
9
42
【数据范围】
对于 20%:N<=10
对于 50%:N<=100
对于另外 20%:K = 0
对于 100%:1 <= N <= 100000,0 <= K <= N,M <= 10^14,1 <= b[i] <= a[i] <= 10^9
分析:
一、对 30%数据
枚举每一个位置作为时间点,设第 i 个点为分割点。
利用田忌赛马的贪心策略,对于前面 1~i-1 的数字从大到小排序,后面的从小到大排序 用自己最大的牌与前面最大的比较,如果能胜利则 ans++,且丢掉自己的一张牌 用自己最小的牌与后面最小的比较,如果能胜利则 ans++,且丢掉自己的一张牌
二对于 100%的数据
贪心部分与 30 分数据差不多 但找分割点,可以 set,把自己的卡片加入到 set,每次拿正好 大一点的数,用完删除 正着做一次,反着做一次,然后选一次每个点作为分割点的得分
代码:
#include<bits/stdc++.h>
using namespace std;
set<int> l,r;
#define N 50010
int a[N],g[N],f[N],n,w[N*2],ans;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
w[a[i]]=1;
}
for(int i=1;i<=2*n;i++){
if(w[i]==0){
l.insert(-i);
r.insert(i);
}
}
for(int i=1;i<=n;i++){
set<int>::iterator it=r.upper_bound(a[i]);//r中第一个比a[i]大的数
if(it!=r.end()){
r.erase(*it);
f[i]=f[i-1]+1;
}
else f[i]=f[i-1];
}
for(int 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];
}
for(int i=0;i<=n;i++)
ans=max(ans,f[i]+g[i+1]);
cout<<ans;
return 0;
}