Cardgame
题目背景
有一天 Alice 和 Bob 玩起了纸牌游戏。
他们一共有 \(2n\) 张纸牌,点数分别为 \(1,2,3,...,2n\),每个点数各一张,起初每人会被发到 n 张纸牌。
游戏一共有 \(n\) 轮,每轮 Alice 和 Bob 各出一张牌,谁的点数大谁就赢下了该轮。
但是 Alice 可以使用一次特权,在第 \(i\) 轮使用特权——“规则反转”,表示从第 \(i\) 轮到第 \(n\) 轮,双方玩家比拼谁的点数小,小的一方赢下那一轮。Alice可以在任何一轮使用该特权,当然 Alice 也可以从头到尾不使用该特权,亦或是从第 1 轮就开始使用。
换言之,一旦 Alice 在第 \(i\) 轮使用了特权,那么第 \(1\) 至 \(i?1\) 轮双方玩家比拼谁的点数大,第 \(i\) 至 \(n\) 轮双方玩家比拼谁的点数小。
现在给定 n,并给出 Bob n 轮打出的牌,询问 Alice 如何规划出牌和运用特权来赢取最多轮数。
输入格式
输入共 \(n+1\) 行。
第一行一个整数 n 。
接下来的 n 行每行一个在 \([1,2n]\) 范围内的正整数且各不相同,表示 Bob 每一轮出的牌的点数。
输出格式
输出一行一个整数表示 Alice 可能赢取的最多轮数。
样例
input
4
1
8
4
3
output
3
explanation
Alice 拥有的手牌是 \(2,5,6,7\)。她可以\(7,6,5,2\)这样出牌,并在第二轮使用特权。
数据范围
时间限制: 1s
空间限制: 256MB
对于 100% 的数据, \(n≤50000\)。
数据富含梯度。
把所有牌求出来后按照大小排序。不考虑特权,我们把Alice所有的牌放在一个set里,每次遇到Bob的牌找到一个最小的又比他大的,这样子就可以求出不使用特权的最多可能。
考虑使用特权,我们可以发现在\(i\)处使用特权,肯定是用前\(i\)大的放在前面,剩下的放在后面。那么其实我们按照刚刚的方法也可以预处理出只用前\(i\)大的最大赢的次数,是一个前缀和。同理,也可以求出一个后缀和有关用前\(i\)小的,然后加起来求个最小值就可以了。
#include
using namespace std;
const int N=50005;
sets1,s2;
int n,v[N<<1],f[N],g[N],a[N],ans;
set::iterator it;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i),v[a[i]]=1;
for(int i=1;i<=(n<<1);i++)
{
if(!v[i])
{
s1.insert(i);
s2.insert(-i);
}
}
for(int i=1;i<=n;i++)
{
f[i]=f[i-1],it=s1.lower_bound(a[i]);
if(it!=s1.end())
{
f[i]++;
s1.erase(it);
}
}
for(int i=n;i>=1;i--)
{
g[i]=g[i+1],it=s2.lower_bound(-a[i]);
if(it!=s2.end())
{
g[i]++;
s2.erase(it);
}
}
for(int i=0;i<=n;i++)
ans=max(ans,f[i]+g[i+1]);
printf("%d",ans);
return 0;
}