概率與期望專題題解
未完待續...
Race to 1 Again
題目連結
題目大意
Rimi learned a new thing about integers, which is - any positive integer greater than 1 can be divided by its divisors. So, he is now playing with this property. He selects a number N. And he calls this D.
In each turn he randomly chooses a divisor of D (1 to D). Then he divides D by the number to obtain new D. He repeats this procedure until D becomes 1. What is the expected number of moves required for N to become 1.給你一個整數,每一次操作可以除以包括當前這個數在內的一個因子,除以每個數都是等概率的,問將n變為1的期望步數
測試資料有多組
\(1 ≤ N ≤ 10^5\)
題解
如果將\(dp[i]\)視為噹噹前值為\(i\)時,將i變為1的期望步數, 很容易就可以想到一個遞推式子
\(dp[1]=0\)
\(Ans=dp[n]\)
\(dp[i]=\frac{dp[p_1] + dp[p_2] + dp[p_3] + ... + dp[i]}{cnt}\)
其中p為i的所有因子,cnt為因子的個數
但是由於在這個式子中,dp[i]可以被他自己給轉移,所以我們肯定不能用這個式子來求最終的答案
不難想到,可以通過將\(dp[i]\)
\(cnt*dp[i]=dp[p_1] + dp[p_2]+...+dp[p_{cnt-1}]+dp[i]\)
\((cnt - 1)*dp[i]=dp[p_1] + dp[p_2]+...+dp[p_{cnt-1}]\)
\(dp[i]=\frac{dp[p_1] + dp[p_2]+...+dp[p_{cnt-1}]}{cnt - 1}\)
於是,我們就可以用一個遞推來愉快的來求答案了
時間複雜度\(O(n\sqrt{n})\)
程式碼
#include <cstdio> #include <cmath> const int N = 1e5; int n, kase; double dp[N + 10]; int main() { // 先預處理答案 dp[1] = 0; for (int i = 2; i <= N; i ++) { double x = 0; int cnt = 0; for (int j = 1; j <= sqrt(i); j ++) if (i % j == 0) { ++ cnt, x += dp[j]; if (i / j != j) ++ cnt, x += dp[i / j]; } dp[i] = (x + cnt) / ((cnt - 1) * 1.0); } // 多組測試資料 int T; scanf("%d", &T); while (T --) { scanf("%d", &n); printf("Case %d: %.7lf\n", ++ kase, dp[n]); } return 0; }
注意點
- n的因子包括1和他本身
Collecting Bugs
程式碼
#include <cstdio>
const int N = 1000;
int n, s;
double dp[N + 10][N + 10];
int main() {
scanf("%d%d", &n, &s);
double p0 = n * s;
for (int i = n; i >= 0; i --) {
for (int j = s; j >= 0; j --) if (i != n || j != s) {
dp[i][j] = (double) (dp[i][j + 1] * i * 1.0 / n * (1 - j * 1.0 / s)
+ dp[i + 1][j] * (1 - i * 1.0 / n) * j * 1.0 / s
+ dp[i + 1][j + 1] * (1 - i * 1.0 / n) * (1 - j * 1.0 / s) + 1) / (1 - i * j / p0);
}
}
printf("%.4lf\n", dp[0][0]);
return 0;
}
注意點
- 由於是poj的題,且poj的g++不支援使用%lf,所以如果你使用了%lf來輸出,必須要提交到c++裡
One Person Game
題目連結
程式碼
#include <cstdio>
#include <cstring>
const int N = 500;
double a[N * 2], b[N * 2], p[100];
int n, k1, k2, k3, x, y, z;
int main() {
int T; scanf("%d", &T);
while (T --) {
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(p, 0, sizeof(p));
scanf("%d%d%d%d%d%d%d", &n, &k1, &k2, &k3, &x, &y, &z);
p[0] = 1.0 / (k1 * k2 * k3);
for (int i = 1; i <= k1; i ++) for (int j = 1; j <= k2; j ++) for (int k = 1; k <= k3; k ++)
if (i != x || j != y || k != z)
p[i + j + k] += p[0];
for (int i = n; i >= 0; i --) {
for (int j = 3; j <= k1 + k2 + k3; j ++) {
a[i] += a[i + j] * p[j];
b[i] += b[i + j] * p[j];
}
a[i] += p[0];
b[i] += 1;
}
printf("%.9lf\n", b[0] / (1 - a[0]));
}
return 0;
}
Check the difficulty of problems
程式碼
#include <cstdio>
#include <cstring>
const int T = 1001;
const int M = 31;
int m, t, n;
double dp[T][M][M], p[T][M];
int main() {
while (scanf("%d%d%d", &m, &t, &n) && m && t && n) {
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= t; i ++) for (int j = 1; j <= m; j ++) scanf("%lf", &p[i][j]);
for (int i = 1; i <= t; i ++) dp[i][0][0] = 1;
for (int i = 1; i <= t; i ++) {
for (int j = 1; j <= m; j ++) {
for (int k = 0; k <= j; k ++) {
if (k > 0) dp[i][j][k] += dp[i][j - 1][k - 1] * p[i][j];
if (k < j) dp[i][j][k] += dp[i][j - 1][k] * (1 - p[i][j]);
}
}
}
double p1 = 1, p2 = 1;
for (int i = 1; i <= t; i ++) {
p1 *= 1 - dp[i][m][0];
double tmp = 0;
for (int j = 1; j < n; j ++) tmp += dp[i][m][j];
p2 *= tmp;
}
printf("%.3lf\n", p1 - p2);
}
return 0;
}
注意點
- 由於是poj的題,且poj的g++不支援使用%lf,所以如果你使用了%lf來輸出,必須要提交到c++裡
Bag of mice
程式碼
#include <cstdio>
const int N = 1000;
int w, b;
double dp[N + 10][N + 10];
int main() {
scanf("%d%d", &w, &b);
for (int i = 1; i <= w; i ++) dp[i][0] = 1;
for (int i = 1; i <= w; i ++) {
for (int j = 1; j <= b; j ++) {
dp[i][j] = i * 1.0 / (i + j);
double k = j * 1.0 / (i + j) * (j - 1) / (i + j - 1);
if (i >= 1 && j >= 2) {
dp[i][j] += dp[i - 1][j - 2] * k * i * 1.0 / (i + j - 2);
}
if (j >= 3) {
dp[i][j] += dp[i][j - 3] * k * (j - 2) * 1.0 / (i + j - 2);
}
}
}
printf("%.9lf\n", dp[w][b]);
return 0;
}
Journey
程式碼
#include <cstdio>
const int N = 100000;
int n, head[N + 10], nxt[N * 2 + 10], to[N * 2 + 10], tot, v[N + 10];
double d[N + 10];
void add(int x, int y) {
nxt[++ tot] = head[x];
head[x] = tot;
to[tot] = y;
}
void dfs(int x, int l) {
v[x] = true;
int cnt = 0;
double k = 0;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
if (v[y]) continue;
dfs(y, l + 1);
++ cnt;
k += d[y];
}
if (cnt == 0) {
d[x] = l;
} else {
d[x] = 1.0 / cnt * k;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i ++) {
int x, y; scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
dfs(1, 0);
printf("%.8lf", d[1]);
return 0;
}