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;
}

 

posted @ 2018-10-01 23:46  Stargazer_cyk  阅读(196)  评论(0编辑  收藏  举报