600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > #1489 : Legendary Items

#1489 : Legendary Items

时间:2020-09-30 10:48:00

相关推荐

#1489 : Legendary Items

原题链接

时间限制: 10000ms 单点时限: 1000ms 内存限制: 256MB

描述

Little Hi is playing a video game. Each time he accomplishes a quest in the game, Little Hi has a chance to get a legendary item.

At the beginning the probability isP%. Each time Little Hi accomplishes a quest without getting a legendary item, the probability will go upQ%. Since the probability is getting higher he will get a legendary item eventually.

After getting a legendary item the probability will be reset to ⌊P/(2I)⌋% (⌊x⌋ represents the largest integer no more than x) where I is the number of legendary items he already has. The probability will also go upQ%each time Little Hi accomplishes a quest until he gets another legendary item.

Now Little Hi wants to know the expected number of quests he has to accomplish to get N legendary items.

AssumeP= 50,Q= 75 andN= 2, as the below figure shows the expected number of quests is

2*50%*25% + 3*50%*75%*100% + 3*50%*100%*25% + 4*50%*100%*75%*100% = 3.25

输入

The first line contains three integersP,QandN.

1 ≤ N ≤ 106, 0 ≤ P ≤ 100, 1 ≤ Q ≤ 100

输出

Output the expected number of quests rounded to 2 decimal places.

样例输入 50 75 2 样例输出 3.25 解题思路: 参考链接

提示也隐藏在这张图中,我们仔细观察可以发现红圈中的子树和黄圈中的子树一模一样。

这提示我们,无论第一件传说物品是怎么获得的(完成第一件任务就获得还是完成两件任务后获得),第二件传说物品都是从25%概率开始,与第一件无关。换句话说,每件传说物品的获得都是独立的,就好像掷N枚骰子每枚骰子的点数是相互独立的一样。

我们知道如果X和Y是两个独立的随机变量,那么E(X+Y)=E(X)+E(Y)。于是我们可以分别求出获得第一件、第二件……第N件传说物品的期望任务数,再把它们加起来就是最终答案。

我们知道第i件传说物品起始的概率是⌊P/(2i-1)⌋%,为了描述方便把它记作Pi;任务失败后概率增加Q%。用二叉树表示大概是这样:

由于概率不断增加Q%,我们最多完成100个任务就一定能获得第i件传说物品。所以计算第i件传说物品的期望任务数的复杂度是O(100)。一般我们在计算复杂度时是忽略常数的,不过这里100次计算是一个比较大的常数,为了体现这一点我们姑且把这个复杂度记作O(100)。这样计算N件传说物品的总复杂度就是O(100N)的。这个算法已经相当不错了,大概能得到90/100分。

代码如下:

#include<iostream>#include<stdio.h>#include<algorithm>#include<math.h>using namespace std;int main() {//freopen("input.txt","r",stdin);int p,q,n;cin>>p>>q>>n;double res = 0.0;int p1;for(int i = 1; i <= n; i++) {if(i < 8) p1 = floor(p / pow(2,i-1));else p1 = 0;double p2 = 1.0;for(int j = 1; j <= 100; j++) {//路经长度res += p1 / 100.0 * p2 * j;if(p1 == 100) break;p2 *= (1 - p1 / 100.0);p1 = p1 + q;if(p1 > 100) p1 = 100;}}printf("%.2f\n",res);return 0;}

如果我们进一步分析,会发现虽然我们计算了N棵二叉树对应的期望任务数。但这N棵二叉树总共最多有101种形态。因为Q不变,Pi的值唯一决定了这棵二叉树长什么样子,而Pi一共有0~100共101种取值。所以我们只需预先求出起始概率分别是100%、99%、98% … 0%时的期望任务数,保存在数组f[]里。计算第i件传说物品时,根据Pi直接把相应的f[Pi]累加即可。

值得一提的是,我们可以用倒推的方法求出f[],而不用每次O(100)重新计算。

f[100] = 1;for(int i = 99; i >= 0; i--) {int j = min(i + Q, 100); //计算如果这次任务没获得,下一个任务获得的概率//i%的概率1次获得,(1-i%)的概率是从j%起始的期望任务数+1f[i] = i% * 1 + (1-i%) * (f[j] + 1);}

于是我们得到了一个O(100+N)的算法,比之前的O(100N)好了100倍。

如果我们再进一步分析,我们会发现⌊P/(2i-1)⌋%会很快下降到0。实际上从第8件传说物品开始,起始概率就一定都是0了。也就是说从第8件开始我们可以直接用f[0]*(N-7)算出期望任务数的和。这样我们得到了一个更快的O(100+8)的算法。这个算法对于每组数据只需要常数次计算即可得到答案。

上文中的证明:

假设,其中为路径长度的概率。则有

其中,,所以。

代码如下:

#include<iostream>#include<stdio.h>#include<algorithm>#include<math.h>using namespace std;int main() {//freopen("input.txt","r",stdin);int p,q,n;cin>>p>>q>>n;double f[101] = {0};f[100] = 1;for(int i = 99; i >= 0; i--) {int j = min(i+q,100);double pi = i / 100.0; //计算如果这次任务没获得,下一个任务获得的概率//i%的概率1次获得,(1-i%)的概率是从j%起始的期望任务数+1f[i] = pi + (1 - pi) * (f[j] + 1);}double res = 0;int pi ;for(int i = 1; i <= n; i++) {if(i >= 8) pi = 0;else pi = floor(p / pow(2,i-1));res += f[pi];}printf("%.2f\n",res);return 0;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。