概率期望

前言

前几场的GYM遇见了一个概率递推题,发现概率题这一类的出的比较多,这里做一个总结。

牛清楚的裙子!!!

题目链接:牛清楚的裙子!!!
题目大意:有n条裙子。牛清楚穿一号裙获得10000点欢乐值,其他裙子获得1点欢乐值,每次随机穿一条,求问欢乐值的期望。
数据范围: t ≤ 1 e 5 , n ≤ 1 e 7 t\le 1e5,n\le 1e7 t1e5,n1e7
题解:非常经典的概率题了,一般这类随机穿的题目,我们都可以采取 d p dp dp来解决。考虑每次要穿的期望次数。令 f [ i ] f[i] f[i]表示已经穿过i条不同裙子,然后要结束的期望局数,显然有 f [ n ] = 0 f[n]=0 f[n]=0,而我们要求的就是 f [ 0 ] f[0] f[0]。考虑递推式: f [ i ] = i n ∗ ( f [ i ] + 1 ) + n − i n ∗ ( f [ i + 1 ] + 1 ) f[i]=\frac{i}{n}*(f[i]+1)+\frac{n-i}{n}*(f[i+1]+1) f[i]=ni(f[i]+1)+nni(f[i+1]+1)解释下就是有 i i i条穿过的, n − i n-i ni条未穿过的,然后穿一次的转移。我们简化下式子有 f [ i ] = f [ i + 1 ] + n n − i f[i]=f[i+1]+\frac{n}{n-i} f[i]=f[i+1]+nin然后我们知道 f [ n ] = 0 f[n]=0 f[n]=0。所以 f [ n − 1 ] = 0 + n 1 f[n-1]=0+\frac{n}{1} f[n1]=0+1n, f [ n − 2 ] = 0 + n 1 + n 2 f[n-2]=0+\frac{n}{1}+\frac{n}{2} f[n2]=0+1n+2n以此类推 f [ 0 ] = ∑ i = 1 n n i = n ∗ ∑ i = 1 n 1 i f[0]=\sum\limits_{i=1}^{n}{\frac{n}{i}}=n*\sum\limits_{i=1}^{n}{\frac{1}{i}} f[0]=i=1nin=ni=1ni1.即有 n n n条裙子时的期望局数为 f [ 0 ] f[0] f[0],所以每一条裙子期望穿的次数为 f [ 0 ] n = ∑ i = 1 n 1 i \frac{f[0]}{n}=\sum\limits_{i=1}^{n}{\frac{1}{i}} nf[0]=i=1ni1,所以 a n s = ( n + 9999 ) ∗ ∑ i = 1 n 1 i ans=(n+9999)*\sum\limits_{i=1}^{n}{\frac{1}{i}} ans=(n+9999)i=1ni1
d p dp dp数组 O ( n ) O(n) O(n)预处理下 ∑ i = 1 n 1 i \sum\limits_{i=1}^{n}{\frac{1}{i}} i=1ni1,就可以快速求出答案了。
AC代码:

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
const ll N = 10000000 + 10;
const int mod = 1e9 + 7;
int t, n;
double dp[N];
int main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	for (int i = 1; i <= 1e7; i++)
	{
		dp[i] = dp[i - 1] + 1.0 / i;
	}
	read(t);
	while (t--)
	{
		read(n);
		double ans = (n + 9999)* dp[n];
		printf("%.7lf\n", ans);
	}
	return 0;
}

A. Sticker Album

题目链接:Sticker Album
题目大意:要集满 n n n个邮票,一个包裹里面有 a − b a-b ab个邮票(均匀的整数分布)。求集满 n n n个邮票期望要的包裹数量。
数据范围: 1 ≤ n ≤ 1 e 6 , 0 ≤ a , b ≤ 1 e 6 1\le n\le 1e6,0\le a,b\le 1e6 1n1e6,0a,b1e6
题解:也是应该概率递推题。我们设 d p [ i ] dp[i] dp[i]为手中已经有了 i i i个邮票要集满的期望包裹数。 d p [ n ] = 0 , d p [ 0 ] dp[n]=0,dp[0] dp[n]=0,dp[0]即是答案。我们考虑如何转移。 d p [ i ] = 1 b − a + 1 ∑ j = a b ( d p [ i + j ] + 1 ) = 1 + 1 b − a + 1 ∑ j = a b ( d p [ i + j ] ) dp[i]=\frac{1}{b-a+1}\sum\limits_{j=a}^{b}(dp[i+j]+1)=1+\frac{1}{b-a+1}\sum\limits_{j=a}^{b}(dp[i+j]) dp[i]=ba+11j=ab(dp[i+j]+1)=1+ba+11j=ab(dp[i+j])。记录一下前缀和然后转移即可?我们需要去注意 a = 0 a=0 a=0的情况。当 a = 0 a=0 a=0时,转移方程是 d p [ i ] = 1 + 1 b + 1 ∑ j = 0 b ( d p [ i + j ] ) = 1 + 1 b + 1 ∑ j = 1 b ( d p [ i + j ] ) + 1 b + 1 d p [ i ] dp[i]=1+\frac{1}{b+1}\sum\limits_{j=0}^{b}(dp[i+j])=1+\frac{1}{b+1}\sum\limits_{j=1}^{b}(dp[i+j])+\frac{1}{b+1}dp[i] dp[i]=1+b+11j=0b(dp[i+j])=1+b+11j=1b(dp[i+j])+b+11dp[i],这里的转移两边都有 d p [ i ] dp[i] dp[i],我们需要特判下,将等式右边的 d p [ i ] dp[i] dp[i]挪到左边。剩下的就是记录前缀和然后对应转移了。
AC代码:

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
const ll N = 2000000 + 10;
const int mod = 1e9 + 7;
int n,a,b;
double dp[N],sum[N];
int main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	read(n), read(a), read(b);
	int len = b - a + 1;
	dp[n] = 0.0;
	for (int i = n - 1; i >= 0; i--)
	{
		if (!a)
		{
			dp[i] = (sum[i + 1] - sum[i + b + 1]+len) / (len * 1.0);
			dp[i] = dp[i] * (1.0*len) / (1.0 * len - 1);
		}
		else
		{
			dp[i] = (sum[i + a] - sum[i + b + 1]+len) / (len * 1.0);
		}
		sum[i] = sum[i + 1] + dp[i];
	}
	printf("%lf\n", dp[0]);
	return 0;
}

P4550 收集邮票

题目链接:收集邮票
题目大意:有n种不同的邮票,皮皮想收集所有种类的邮票。唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n。但是由于凡凡也很喜欢邮票,所以皮皮购买第k张邮票需要支付k元钱。
现在皮皮手中没有邮票,皮皮想知道自己得到所有种类的邮票需要花费的钱数目的期望。
数据范围: 1 ≤ n ≤ 1 e 4 1\le n\le 1e4 1n1e4
题解:好题!求出期望要买 x x x张邮票,那么要花的钱期望为 ( x + 1 ) x 2 = x 2 + x 2 \frac{(x+1)x}{2}=\frac{x^2+x}{2} 2(x+1)x=2x2+x。套路的设计 d p [ i ] dp[i] dp[i]表示已经已经集齐 i i i种邮票,集满期望的次数。然后将 d p [ 0 ] dp[0] dp[0]带入 x x x,完美结束。。。。。等等,好像样例都不过去。。。。问题在哪?我们重新看 x 2 + x 2 \frac{x^2+x}{2} 2x2+x这个式子,其中的 x 2 x^2 x2表示的是局数平方的期望,而我们直接将 x x x带入求出的 x 2 x^2 x2表示的是局数期望的平方。所以我们不能直接带入,还需要求出一个 d p 2 [ i ] dp2[i] dp2[i]表示已经已经集齐 i i i种邮票,集满次数平方的期望。然后将 d p 2 [ 0 ] dp2[0] dp2[0] d p [ 0 ] dp[0] dp[0]再带入就行了。
AC代码:

#include<bits/stdc++.h>

#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
	T res = 0, f = 1; char c = getchar();
	while (!isdigit(c)) {
		if (c == '-')f = -1; c = getchar();
	}
	while (isdigit(c)) {
		res = (res << 3) + (res << 1) + c - '0'; c = getchar();
	}
	x = res * f;
}
const ll N = 200000 + 10;
const int mod = 1e9 + 7;
int n;
double ans,f[N],f2[N];
int main()
{
	//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
	freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
	read(n);
	double ans=0;
	for (int i = n - 1; i >= 0; i--)
	{
		f[i] = f[i + 1] + 1.0*n / (n - i);
		f2[i] = 2.0 * i / (1.0 * n - i) * f[i] + f2[i + 1] + 2.0 * f[i + 1] + 1.0*n / (1.0 * n - i);
	}
	printf("%.2lf\n", (f[0] + f2[0]) / 2);
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值