最近, 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
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 n−1 个青年。一旦 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} i−1k
总的概率是 ∑ 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+1∑nn(i−1)k=nki=k∑n−1i1
由于 k n \dfrac{k}{n} nk 是递增的, ∑ i = k n − 1 1 i \sum\limits_{i=k}^{n-1}\dfrac1i i=k∑n−1i1 是递减的。所以这个概率是单峰函数。可以用三分求出使其最大的 k k k。
当然 ∑ i = 1 n 1 i \sum\limits_{i=1}^n\dfrac1i i=1∑ni1 近似于 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
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);
}
}