【NOIP校内模拟/Usaco2015 dec—High Card Low Card】cardgame

【题意】

L最近喜欢上了一个卡片游戏,游戏规则是: 2 个人一共拿 2n 张卡片,编号 1..2n,每个人 n 张,然后进行 n 轮出牌,每轮 2 个 人都打一张牌,,点数大的玩家每次获 1分。 L可以预测到对方要打牌的顺序。 同时,L有一次机会选择了某个时间点,从那个时候开始,每回合点数少者获胜。
请你帮助 L获得最大的分数

我们用贪心来做

假设规则没有变的时候 对于对方打出的一张牌 L从他的牌里打出一张刚好比这张牌大的牌显然是最优的

假设规则变了的时候 对于对方打出的一张牌 L从他的牌里打出一张刚好比这张牌小的显示是最优的

似乎这个地方也有另一种贪心的方法 对于每一张牌 找一个最大的比他的牌也是可行的 因为这样在变规则后 可以使得剩下的牌更好得分

我们考虑如何实现

我们维护两个数组 front back,front[i]:没变规则时,1~i轮能得到的最大分数 back[i]:变了规则后,i~n轮能得到的最大分数

答案为max{front[i]+back[i+1]}

很多人都会提到说 可能一张牌在点数大的赢的时候被用了,后面改规则后也被用了,就被重复用了两次。

但是其实没有关系。如果一张牌a被用了两次,那么就说明手中有一张牌设为b没有被用过。若a>b,那么在点数小的赢那轮可以用b去替代a来赢得胜利;反之亦然。

最后 数组记得开100000 不然会炸成30分

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,cnt;
int t[N],card[N],sx[N];
int front[N],back[N];
bool used[N];
int find_upper(int x)
{
    int l=0,r=n;
    while(l<=r)
    {
        int m=(l+r)>>1;
        if(card[m]>x)   r=m-1;
        else l=m+1; 
    }
    return l;
}
int find_lower(int x)
{
    int l=0,r=n;
    while(l<=r)
    {
        int m=(l+r)>>1;
        if(card[m]>x)   r=m-1;
        else l=m+1; 
    }
    return l-1;
}
int main()
{
    freopen("cardgame.in","r",stdin);
    freopen("cardgame.out","w",stdout); 
    memset(used,false,sizeof(used));
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>sx[i];
        t[sx[i]]++;
    }
    for(int i=1;i<=2*n;i++)
    {
        if(!t[i])   card[++cnt]=i;
    }
    for(int i=1;i<=n;i++)
    {
        int pos=find_upper(sx[i]);
        while(used[pos]==true)  pos++;  
        if(pos>n)   front[i]=front[i-1];
        else 
        {
            used[pos]=true;
            front[i]=front[i-1]+1;  
        }       
    }
    //for(int i=1;i<=n;i++) cout<<front[i]<<" ";
    memset(used,false,sizeof(used));
    for(int i=n;i>=1;i--)
    {
        int pos=find_lower(sx[i]);
        while(used[pos]==true)  pos--;
        if(pos<1)   back[i]=back[i+1];
        else {
            used[pos]=true;
            back[i]=back[i+1]+1;
        }
    }
    //for(int i=1;i<=n;i++) cout<<back[i]<<" ";
    int ans=0;
    for(int i=0;i<=n;i++)
    {
        ans=max(front[i]+back[i+1],ans);
    }
    cout<<ans;
    return 0;
}

转载于:https://www.cnblogs.com/Patrickpwq/articles/9734746.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值