题目概览
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)题
题目链接
在n个门前选择一扇门出去, 如果第i扇门的 Xi 为正,会在 Xi 时间后离开迷宫,否则会在 |Xi| 后回到初始地方,并且忘记了刚才的选择, 选择每扇门等概率的。计算离开的期望。
定义一次选择到正数的概率为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即可。
#include
#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("inf\n");
else printf("%d/%d\n", sum/g, cnt/g);
}
return 0;
}
题目链接
n个点,每个点有一些金子。从点1开始,每次扔一次6面骰子,向前走点数步,并且拿走那个点的金子,如果当前位置加扔出的点数大于n,就重新扔。询问走到n时,取走的金子的期望。
方案一:从前到后递推
假设 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].
则:
如上进行逆推,最后得到的 f[1] 就是最终的期望值
方案一:
#include
#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("%.10f\n", ans);
}
return 0;
}
方案二:
#include
#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("%.10f\n", dp[1]);
}
return 0;
}
题目链接
给出一个数N,每次可以选择N的一个因子,用N除以这个因子得到一个新的数,计算 N 变为1的操作次数的期望。
求操作次数的期望时,假设第 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
#include
#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("%.8f\n", dp[n]);
}
return 0;
}
题目链接
给定一个 n 面的骰子,计算看到所有的面至少一次所需掷骰子次数的期望
方案一:概率DP
假设 dp[i] 表示当前已经出现 i 个面时的期望次数,那么当前 dp[i] 可以由一下状态进行转移:
综上
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
对于本题来说:
所以每个面至少出现一次的期望为
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
DP做法
#include
#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("%.8f\n", dp[n]);
}
return 0;
}
数学做法
#include
#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("%.8f\n", ans);
}
return 0;
}
题目链接
你在一个有t只老虎和d只鹿的荒岛上,计算生还几率,每天有两只动物相遇,鹿、老虎、你随机相遇:
如果你和老虎相遇,老虎会杀了你。
如果老虎和鹿相遇,老虎会吃掉鹿。
如果两只鹿见面,什么都没发生。
如果你遇见一只鹿,你可能会也可能不会杀鹿(取决于你)。
如果两只老虎相遇,他们会同归于尽。
概率DP写多了,被这题给搞了…
对你来说,威胁者只有老虎,对老虎来说,威胁者也只有老虎,所以鹿的存在对你的存活率毫无意义。
对于 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
#include
#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("%.8f\n", ans);
}
return 0;
}