【题目】
题目描述:
Alice 和 Bob 两个人正在玩一个游戏,游戏有很多种任务,难度为 p p p 的任务( p p p 是正整数),有 1 2 p \frac{1}{2^p} 2p1 的概率完成并得到 2 p − 1 2^{p-1} 2p−1 分,如果完成不了,得 0 0 0 分。一开始每人都是 0 0 0 分,从 Alice 开始轮流做任务,她可以选择任意一个任务来做;而 Bob 只会做难度为 1 1 1 的任务。只要其中有一个人达到 n n n 分,即算作那个人胜利。求 Alice 采取最优策略的情况下获胜的概率。
输入格式:
一个正整数 n n n ,含义如题目所述。
输出格式:
一个数,表示 Alice 获胜的概率,保留 6 6 6 位小数。
样例数据:
输入
1
输出
0.666667
备注:
【数据范围】
对于
30
%
30\%
30% 的数据,
n
n
n ≤
10
10
10
对于
100
%
100\%
100% 的数据,
n
n
n ≤
500
500
500
【分析】
概率 d p dp dp 入门题
定义 f i , j f_{i,j} fi,j 表示 Alice 得了 i i i 分,Bob 得了 j j j 分后 Alice 获胜的概率
那么初始化 f n , i = 1 f_{n,i}=1 fn,i=1( 0 0 0 ≤ i i i ≤ n n n),最后的答案 a n s ans ans 会在 f 0 , 0 f_{0,0} f0,0 中
现在就考虑如何通过后面的概率来转移 f i , j f_{i,j} fi,j
我们枚举 Alice 下一步的得分(由于 Bob 只有 0 0 0 和 1 1 1 两种分值,不用枚举)
如果下一步的任务难度为 p p p,令 k = 2 p − 1 k=2^{p-1} k=2p−1,那么拿到这 k k k 分的概率就是 1 2 p \frac{1}{2^p} 2p1(即 1 2 ⋅ k \frac{1}{2\cdot k} 2⋅k1),不得分的概率是 2 ⋅ k − 1 2 ⋅ k \frac{2\cdot k-1}{2\cdot k} 2⋅k2⋅k−1
那么转移方程就是: f i , j = f i + k , j 4 k + f i + k , j + 1 4 k + f i , j ⋅ ( 2 k − 1 ) 4 k + f i , j + 1 ⋅ ( 2 k − 1 ) 4 k f_{i,j}=\frac{f_{i+k,j}}{4k}+\frac{f_{i+k,j+1}}{4k}+\frac{f_{i,j}\cdot(2k-1)}{4k}+\frac{f_{i,j+1}\cdot (2k-1)}{4k} fi,j=4kfi+k,j+4kfi+k,j+1+4kfi,j⋅(2k−1)+4kfi,j+1⋅(2k−1)
把右边的 f i , j f_{i,j} fi,j 移到左边来的话,就是 f i , j = ( f i + k , j ) + ( f i + k , j + 1 ) + ( f i , j + 1 ⋅ ( 2 k − 1 ) ) 2 k + 1 f_{i,j}=\frac{(f_{i+k,j})+(f_{i+k,j+1})+({f_{i,j+1}\cdot (2k-1)})}{2k+1} fi,j=2k+1(fi+k,j)+(fi+k,j+1)+(fi,j+1⋅(2k−1))
应该还是比较好懂的吧
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 505
using namespace std;
double f[N][N];
int main()
{
int n,i,j,k;
scanf("%d",&n);
for(i=0;i<=n;++i)
f[n][i]=1;
for(i=n-1;i>=0;--i)
for(j=n-1;j>=0;--j)
for(k=1;k/2<=n;k<<=1)
{
int next=min(i+k,n);
f[i][j]=max(f[i][j],(f[next][j]+f[next][j+1]+f[i][j+1]*(2*k-1))/(2.0*k+1.0));
}
printf("%.6lf",f[0][0]);
return 0;
}