game【省选模拟赛】

29 篇文章 0 订阅
4 篇文章 0 订阅

标签:博弈论,二分

题目

给定 n 个数字,两个人轮流删去一个,到剩余两个数字结束,第一个人最大化差,第二个人最小化,问最后的差是多少。

分析

先考虑两种特殊情况(subtask)

  • 当n为奇数,最大化的那个人先手行动时,答案出现的形式肯定是最大化的从中间拿掉一个,最小化的从两边拿掉
  • 答案就是最小的一对 abs(a[i]a[i+n2]) a b s ( a [ i ] − a [ i + ⌈ n 2 ⌉ ] )
  • 证明这个答案是正确的,显然这个状态对于最大化的是可达的,对于最小化的来说,考虑一共有 n21 ⌊ n 2 ⌋ − 1 次操作机会,只需要选定这一对,然后把外面的全删掉就可行了

  • 当n为奇数,最小化的那个人先手行动
  • 考虑二分的做法,对于最小化的人来说,每次二分来检验是否可以将答案限制在二分值之下
  • 至于判断和检验的话,直接贪心将石子划分成最少的堆就好了,如果堆的数量不超过 n2 ⌈ n 2 ⌉ 就符合条件了
  • 证明可以用数学归纳法,我们考虑证明最终只会剩下一堆,可以通过归纳证明,当还剩 i i 步的时候,石子不会超过 i+1 i + 1 堆。发现石子个数恰好是 2i+1 2 i + 1 个,如果现在恰好堆是满的,那么一定有一堆只有一个,删去即可,完成归纳;不可达的证明,我们可以考虑贪心的时候,从前往后分,那么任意两个堆头一定距离超过二分值,那么就不可能把堆删光,于是得证。

另外的两种情况,直接判断下就可以了,和前两种特殊情况对应的

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
#define ll long long
#define mem(x,num) memset(x,num,sizeof x)
using namespace std;
inline ll read(){
    ll f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
#define inf 1e9
const int maxn=2e5+6;
int n,a[maxn],f[maxn];
char s[maxn];
inline bool check(int x){
    int last=1;
    rep(i,1,n){
        while(a[i]-a[last]>x)last++;
        f[i]=f[last-1]+1;
    }
    return f[n]<=(n+1)/2;
}
int main(){
    freopen("game.in","r",stdin);
    freopen("game.out","w",stdout); 
    while(scanf("%d%s",&n,s)!=EOF){
        rep(i,1,n)a[i]=read();
        int last=(n&1)^(s[0]=='s');
        if(!last){
            int ans=inf;
            rep(i,1,n-(n+1)/2)ans=min(ans,a[i+(n+1)/2]-a[i]);
            cout<<ans<<endl; 
        }else{
            int l=0,r=inf;
            while(l<r){
                int mid=(l+r)>>1;
                if(check(mid))r=mid;else l=mid+1;
            }
            cout<<l<<endl;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值