概述
题目概览
LightOJ - 1027 A Dangerous Maze
LightOJ - 1030 Discovering Gold:概率dp
LightOJ - 1038 Race to 1 Again:统计因子的贡献
LightOJ - 1248 Dice (III) :概率dp 或 几何分布
LightOJ - 1265 Island of Survival:简单O(1)题
前置技能
- 期望可以分解成多个子期望的加权和,权为子期望发生的概率,即 E ( a A + b B + . . . ) = a E ( A ) + b E ( B ) + . . . + 1 E(aA + bB+...) = aE(A) + bE(B) + ... + 1 E(aA+bB+...)=aE(A)+bE(B)+...+1
LightOJ - 1027 A Dangerous Maze
题目链接
Description
在n个门前选择一扇门出去, 如果第i扇门的 Xi 为正,会在 Xi 时间后离开迷宫,否则会在 |Xi| 后回到初始地方,并且忘记了刚才的选择, 选择每扇门等概率的。计算离开的期望。
Solution
定义一次选择到正数的概率为P1,选择了正数后离开的平均时间为T1,选择到负数的概率是P2, 选择了负数后回到初始位置的平均时间为T2。设离开的期望为Y。
Y
=
P
1
∗
T
1
+
P
2
∗
(
T
2
+
Y
)
Y = P1 * T1 + P2 * (T2 + Y)
Y=P1∗T1+P2∗(T2+Y)
Y
=
P
1
∗
T
1
+
P
2
∗
T
2
P
1
Y = frac{P1*T1 + P2*T2}{P1}
Y=P1P1∗T1+P2∗T2
经过整理
Y
=
∑
i
=
1
N
a
b
s
(
X
i
)
正
数
的
个
数
Y=frac{sum _{i = 1} ^{N} abs(Xi)}{正数的个数}
Y=正数的个数∑i=1Nabs(Xi)
最后对答案取个gcd即可。
Code
#include <cstdio>
#define rush() int T;scanf("%d", &T);for(int cas = 1; cas <= T; cas++)
int main()
{
rush() {
int n;
scanf("%d", &n);
int cnt = 0, sum = 0, x;
for(int i = 1; i <= n; i++) {
scanf("%d", &x);
sum += abs(x);
if(x > 0) cnt++;
}
int g = __gcd(sum, cnt);
printf("Case %d: ", cas);
if(cnt == 0) printf("infn");
else printf("%d/%dn", sum/g, cnt/g);
}
return 0;
}
LightOJ - 1030 Discovering Gold
题目链接
Description
n个点,每个点有一些金子。从点1开始,每次扔一次6面骰子,向前走点数步,并且拿走那个点的金子,如果当前位置加扔出的点数大于n,就重新扔。询问走到n时,取走的金子的期望。
Solution
方案一:从前到后递推
假设 dp[i] 为到第i个点的概率,a[i]为第i个格子的金子数量。那么期望就是
∑
d
p
[
i
]
∗
a
[
i
]
sum dp[i] * a[i]
∑dp[i]∗a[i]
对于初始点1来说,dp[1] = 1,否则,dp[i] = dp[j] * 骰子摇出点数为 j-i 的概率
方案二:从后到前倒退
那么当前的状态等于 所有后继状态的期望 * 到达它的概率 进行求和。
由于走到的点是一定能拿到的,因此令每个的点的初始期望值等于每个点的初始值,即:dp[i] = a[i].
则:
- 在第 n 个点,获得的金子数目为 dp[n]
- 在第 n-1 个点,掷骰子有一种情况,获得的数目为: dp[n-1] + dp[n]
- 在第 n-2 个点,掷骰子有两种情况,获得的数目为: dp[n-2] + dp[(n-2)+1]/2 + dp[(n-2)+2]/2
- …
- 在第 i 个点,掷骰子有六种情况,获得的数目为: dp[i] = dp[i] + dp[i+1]/6 + dp[i+2]/6 + dp[i+3]/6 + dp[i+1]/6 + dp[i+5]/6 + dp[i+6]/6
如上进行逆推,最后得到的 f[1] 就是最终的期望值
Code
方案一:
#include <cstdio>
#define rush() int T;scanf("%d", &T);for(int cas = 1; cas <= T; cas++)
double a[105];
double dp[105], cnt[105];
int main()
{
rush() {
printf("Case %d: ", cas);
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%lf", a + i);
dp[i] = 0;
}
dp[1] = 1.0;
double ans = 0.0;
for(int i = 1; i <= n; i++) {
for(int j = max(i - 6, 1); j < i; j++) {
int k = min(6, n - j);
dp[i] += dp[j] / k; // 当前点可掷出的点数共k种,其中可以由j点到达i点只有一种
}
ans += dp[i] * a[i];
}
printf("%.10fn", ans);
}
return 0;
}
方案二:
#include <cstdio>
#define rush() int T;scanf("%d", &T);for(int cas = 1; cas <= T; cas++)
double a[105];
double dp[105], cnt[105];
int main()
{
rush() {
printf("Case %d: ", cas);
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%lf", a + i);
dp[i] = a[i];
}
for(int i = n; i >= 1; i--) {
for(int j = 1; j <= min(n - i, 6); j++) {
dp[i] += dp[i+j] / min(n - i, 6);
}
}
printf("%.10fn", dp[1]);
}
return 0;
}
LightOJ - 1038 Race to 1 Again
题目链接
Description
给出一个数N,每次可以选择N的一个因子,用N除以这个因子得到一个新的数,计算 N 变为1的操作次数的期望。
Solution
求操作次数的期望时,假设第 i 个因子对当前数期望的贡献次数为
T
i
T_i
Ti,那么有
E
=
(
T
1
+
T
2
+
T
3
+
.
.
.
.
.
.
+
T
n
)
/
n
E = (T_1 + T_2 + T_3 + ...... + T_n) / n
E=(T1+T2+T3+......+Tn)/n.
dp[n]表示 n 除到 1 时的期望,假设 n 的因子有 cnt 个,分别为
a
1
,
a
2
.
.
.
a
c
n
t
a_1, a_2...a_{cnt}
a1,a2...acnt(包括1和n本身),那么
d
p
[
n
]
=
(
d
p
[
a
1
]
+
1
)
+
(
d
p
[
a
2
]
+
1
)
+
.
.
.
+
(
d
p
[
a
c
n
t
]
+
1
)
c
n
t
dp[n]=frac{(dp[a_1]+1)+(dp[a_2]+1)+...+(dp[a_{cnt}]+1)}{cnt}
dp[n]=cnt(dp[a1]+1)+(dp[a2]+1)+...+(dp[acnt]+1)
每个+1代表由 当前因子到n 的期望操作次数需要+1次,其中
a
c
n
t
a_{cnt}
acnt 等于n,故
d
p
[
a
c
n
t
]
dp[a_{cnt}]
dp[acnt] 未知,移项后整理得:,
d
p
[
n
]
=
(
d
p
[
a
1
]
+
1
)
+
(
d
p
[
a
2
]
+
1
)
+
.
.
.
+
(
d
p
[
a
c
n
t
−
1
]
+
1
)
+
1
c
n
t
−
1
dp[n]=frac{(dp[a_1]+1)+(dp[a_2]+1)+...+(dp[a_{cnt-1}]+1)+1}{cnt-1}
dp[n]=cnt−1(dp[a1]+1)+(dp[a2]+1)+...+(dp[acnt−1]+1)+1
Code
#include <cstdio>
#define rush() int T;scanf("%d", &T);for(int cas = 1; cas <= T; cas++)
const int MaxN = 1e6 + 5;
double dp[MaxN];
int main()
{
int tot, p;
double ans;
dp[1] = 0;
for(int i = 2; i <= 1e5; i++) {
tot = 0, ans = 0.0;
for(int j = 1; j * j <= i; j++) {
if(i % j == 0) {
ans += dp[j] + 1;
tot++;
p = i / j;
if(p != j) {
ans += dp[p] + 1; // 当 i/1 == i 时,由于dp[i] = 0,所以没有对答案产生贡献
tot++;
}
}
}
dp[i] = ans / (tot - 1.0);
}
rush() {
printf("Case %d: ", cas);
int n;
scanf("%d", &n);
printf("%.8fn", dp[n]);
}
return 0;
}
LightOJ - 1248 Dice (III)
题目链接
Description
给定一个 n 面的骰子,计算看到所有的面至少一次所需掷骰子次数的期望
Solution
方案一:概率DP
假设 dp[i] 表示当前已经出现 i 个面时的期望次数,那么当前 dp[i] 可以由一下状态进行转移:
- 如果当前出现的第 i 个面是新出现的面,说明当前状态的上一个状态为 dp[i-1],然后前状态 dp[i-1] 从剩下的 (n - i + 1) 个未出现的面中选取了一个作为新出现的面,此时 d p [ i ] = ( n − i + 1 ) / n ∗ d p [ i − 1 ] + 1 dp[i] = (n - i + 1) / n * dp[i-1] + 1 dp[i]=(n−i+1)/n∗dp[i−1]+1
- 如果当前出现的第 i 个面是已经出现过的面,说明当前状态的上一个状态为 dp[i],然后前状态 dp[i] 从上个已经出现的 i-1 个面中选取了一个作为当前出现的面,此时 d p [ i ] = ( i − 1 ) / n ∗ d p [ i ] + 1 dp[i] = (i-1) / n * dp[i] + 1 dp[i]=(i−1)/n∗dp[i]+1
综上
d
p
[
i
]
=
(
n
−
i
+
1
)
/
n
∗
d
p
[
i
−
1
]
+
(
i
−
1
)
/
n
∗
d
p
[
i
]
+
1
dp[i] = (n - i + 1) / n * dp[i-1] + (i-1) / n * dp[i] + 1
dp[i]=(n−i+1)/n∗dp[i−1]+(i−1)/n∗dp[i]+1
化简得
d
p
[
i
]
=
d
p
[
i
−
1
]
+
n
/
(
n
−
i
+
1
)
dp[i] = dp[i-1] + n / (n - i + 1)
dp[i]=dp[i−1]+n/(n−i+1)
方案二:数学 - 几何分布
几何分布的概念:
在第n次伯努利试验中,试验 k 次才得到第一次成功的机率为p。详细的说是:前k-1次皆失败,第k次成功的概率为p。
几何分布的期望 E ( X ) = 1 p E(X) = frac{1}{p} E(X)=p1
对于本题来说:
- 第一个面第一次出现的概率 P 1 = n n P_1=frac{n}{n } P1=nn
- 第二个面第一次出现的概率 P 2 = n − 1 n P_2=frac{n-1}{n } P2=nn−1
- ……
- 第 i 个面第一次出现的概率 P i = n − i + 1 n P_i=frac{n-i+1}{n } Pi=nn−i+1
- ……
- 第 n 个面第一次出现的概率 P n = 1 n P_n=frac{1}{n } Pn=n1
所以每个面至少出现一次的期望为
E
(
X
)
=
∑
i
=
1
n
1
P
i
=
∑
i
=
1
n
n
n
−
i
+
1
E(X)=sum_{i=1}^{n} frac{1}{P_i}=sum_{i=1}^{n}frac{n}{n-i+1}
E(X)=i=1∑nPi1=i=1∑nn−i+1n
Code
DP做法
#include <cstdio>
#define rush() int T;scanf("%d", &T);for(int cas = 1; cas <= T; cas++)
double dp[100005];
int main()
{
rush() {
printf("Case %d: ", cas);
int n;
scanf("%d", &n);
dp[1] = 1.0;
for(int i = 2; i <= n; i++) {
dp[i] = dp[i-1] + 1.0 * n / (n-i+1);
}
printf("%.8fn", dp[n]);
}
return 0;
}
数学做法
#include <cstdio>
#define rush() int T;scanf("%d", &T);for(int cas = 1; cas <= T; cas++)
int main()
{
rush() {
printf("Case %d: ", cas);
int n;
scanf("%d", &n);
double ans = 0;
for(int i = 1; i <= n; i++) {
ans += 1.0 * n / (n - i + 1);
}
printf("%.8fn", ans);
}
return 0;
}
LightOJ - 1265 Island of Survival
题目链接
Description
你在一个有t只老虎和d只鹿的荒岛上,计算生还几率,每天有两只动物相遇,鹿、老虎、你随机相遇:
如果你和老虎相遇,老虎会杀了你。
如果老虎和鹿相遇,老虎会吃掉鹿。
如果两只鹿见面,什么都没发生。
如果你遇见一只鹿,你可能会也可能不会杀鹿(取决于你)。
如果两只老虎相遇,他们会同归于尽。
Solution
概率DP写多了,被这题给搞了…
对你来说,威胁者只有老虎,对老虎来说,威胁者也只有老虎,所以鹿的存在对你的存活率毫无意义。
- 如果没有老虎,不管鹿有几只,存活率都为 1
- 如果有奇数只老虎,存活率为 0,因为不管鹿有几只,一定会有一只老虎剩下来和你相遇
- 如果有偶数只老虎,那么需要两只老虎不停的内耗完才能存活。
对于 t 为偶数时
P
=
C
t
2
C
t
+
1
2
∗
C
t
−
2
2
C
t
−
1
2
∗
C
t
−
4
2
C
t
−
3
2
∗
C
2
2
C
3
2
=
1
t
+
1
P = frac{C^2_{t}}{C^2 _{t+1}} * frac{C^2_{t-2}}{C^2 _{t-1}} * frac{C^2_{t-4}}{C^2 _{t-3}} * frac{C^2 _2}{C^2 _{3}} = frac 1 {t+1}
P=Ct+12Ct2∗Ct−12Ct−22∗Ct−32Ct−42∗C32C22=t+11
Code
#include <cstdio>
#define rush() int T;scanf("%d", &T);for(int cas = 1; cas <= T; cas++)
int main()
{
rush() {
printf("Case %d: ", cas);
int n, t, d;
scanf("%d %d", &t, &d);
double ans;
if(t == 0) ans = 1;
else if(t % 2 == 1) ans = 0;
else ans = 1.0 / (t + 1);
printf("%.8fn", ans);
}
return 0;
}
最后
以上就是灵巧菠萝为你收集整理的【期望】【DP】数学期望总结(ing)前置技能LightOJ - 1027 A Dangerous MazeLightOJ - 1030 Discovering GoldLightOJ - 1038 Race to 1 AgainLightOJ - 1248 Dice (III)LightOJ - 1265 Island of Survival的全部内容,希望文章能够帮你解决【期望】【DP】数学期望总结(ing)前置技能LightOJ - 1027 A Dangerous MazeLightOJ - 1030 Discovering GoldLightOJ - 1038 Race to 1 AgainLightOJ - 1248 Dice (III)LightOJ - 1265 Island of Survival所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复