2023“钉耙编程”中国大学生算法设计超级联赛(2)SPY finding NPY

题目描述

最近, SPY \text{SPY} SPY 已经退出了 XCPC \text{XCPC} XCPC(中国大学生编程竞赛)。但他零开始学习算法并赢得 ICPC \text{ICPC} ICPC(国际大学生编程竞赛)金牌的回忆仍然历历在目,所以他正在寻找一个青年作为他的学徒,取得金牌。 SPY \text{SPY} SPY 非常受欢迎,有 n n n 个青年想成为他的学徒。由于 SPY \text{SPY} SPY只需要一个学徒,所以他给这些青年设置了一个测试。

规则如下:

这些青年从 1 1 1 n n n 编号。 SPY \text{SPY} SPY 将按顺序面试这些青年,第 i i i 个青年将在第 i i i 个面试中接受测试。每个青年面试结束后, SPY \text{SPY} SPY 会得到他/她的智商指数( IQ \text{IQ} IQ,一个范围在 [ 0 , 202 3 202 3 2023 ] [0,2023^{2023^{2023}}] [0,202320232023] 的整数), SPY \text{SPY} SPY 可以决定是否接受他/她。一旦他接受了一个青年,测试就结束了,他不会再面试接下来的青年。一旦他拒绝了一个青年,他就不会再给他/她机会。

注意,没有两个青年具有相同的 IQ \text{IQ} IQ SPY \text{SPY} SPY 有一种特殊的策略来找到一个 IQ \text{IQ} IQ 高的青年。他在测试之前设置一个整数 k k k ( 0 ≤ k < n ) (0≤k<n) (0k<n)

1、无论前 k k k 个青年有多聪明,他们都会被拒绝。 SPY \text{SPY} SPY 会记录前 k k k 个青年中的最高 IQ \text{IQ} IQ 数值 x x x。如果 k = 0 k=0 k=0,则 x = − 1 x=−1 x=1

2、然后他将面试第 k + 1 k+1 k+1 n − 1 n-1 n1 个青年。一旦 SPY \text{SPY} SPY 面试到一个 IQ \text{IQ} IQ 高于 x \text{x} x 的青年,他会接受他/她并结束测试。

3、如果没有青年被接受, SPY \text{SPY} SPY 将接受第 n n n 个青年。

这些青年的 IQ \text{IQ} IQ 排名是随机的,也就是说他们的排名是 1 1 1 n n n 的一个排列,并且 n ! n! n! 种可能的情况发生的概率相等。尽管 SPY \text{SPY} SPY 是一个算法大师,但是他还是很难设定数字 k k k。请你帮助他设定一个数字 k k k,使得他选择最大 IQ \text{IQ} IQ 青年的概率最大。

题解

考虑 k k k 确定时的概率。 k = 0 k=0 k=0 时,概率为 1 n \dfrac1n n1

否则,枚举 IQ \text{IQ} IQ 最高的人的位置 i ∈ ( k , n ] i\in(k,n] i(k,n]。他在这个位置的概率是 1 n \dfrac1n n1

按照题目要求,要使位置 i i i 前面 IQ \text{IQ} IQ 最大的人的位置小于等于 k k k。这样的概率是 k i − 1 \dfrac{k}{i-1} i1k

总的概率是 ∑ i = k + 1 n k n ( i − 1 ) = k n ∑ i = k n − 1 1 i \sum\limits_{i=k+1}^n\dfrac{k}{n(i-1)}=\dfrac{k}{n}\sum\limits_{i=k}^{n-1}\dfrac1i i=k+1nn(i1)k=nki=kn1i1

由于 k n \dfrac{k}{n} nk 是递增的, ∑ i = k n − 1 1 i \sum\limits_{i=k}^{n-1}\dfrac1i i=kn1i1 是递减的。所以这个概率是单峰函数。可以用三分求出使其最大的 k k k

当然 ∑ i = 1 n 1 i \sum\limits_{i=1}^n\dfrac1i i=1ni1 近似于 ln ⁡ n \ln n lnn,概率可以近似看作 k n ln ⁡ n k \dfrac{k}{n}\ln\dfrac{n}{k} nklnkn,经过求导发现 k k k n e \dfrac{n}{e} en 时最大。

因为有误差,所以要在 n e \dfrac ne en 左右都试一遍,取最优的。

下面的代码是求导代码

代码

#include<bits/stdc++.h>
using namespace std;
const double e=exp(1);
int n;
double a[10001];
double f(int x)
{
    if(!x) return 1.0/n;
    return 1.0*x/n*(a[n-1]-a[x-1]);
}
int main()
{
    for(int i=1;i<=10000;i++) a[i]=a[i-1]+1.0/i;
    int t;
    cin>>t;
    while(t--){
        scanf("%d",&n);
        double maxn=0;
        int ans=0;
        for(int i=max(0,int(n/e-1));i<=min(n-1,int(n/e+1));i++){
            if(maxn<f(i)){
                maxn=f(i);
                ans=i;
            }
        }
        printf("%d\n",ans);
    }
}

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值