1. 程式人生 > 其它 >概率與期望專題題解

概率與期望專題題解

未完待續...

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;
}