【POJ 2151Check the difficulty of problems】概率DP

本文解析了POJ 2151题目的求解思路,涉及概率计算、动态规划等方法,阐述了如何计算每个人至少完成一道题且最多个体完成题目数大于指定值的概率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:
http://poj.org/problem?id=2151
题意:
题目中有 T T T个人, M M M道题,做出最多题数要大于等于 N N N,给你一个 p p p矩阵, p [ i ] [ j ] p[i][j] p[i][j]表示第 i i i个人写出第 j j j题的概率。
现求满足每个人写题数量都至少大于等于1并且做题数量最多的人写题数量大于N的概率。
题解:
T T T为全部人的一个集合, f ( t ) f(t) f(t) t t t 这个人的写题数量。
那么题目可以表述为:求满足任意 t ∈ T t∈T tT,都有 f ( t ) > = 1 f(t) >= 1 f(t)>=1且存在 t 0 ∈ T t_0∈T t0T,使得 f ( t 0 ) > = N f(t_0)>=N f(t0)>=N的概率。
设使得条件1成立的概率为 p 1 p_1 p1
使得条件2成立的概率为 p 2 p_2 p2
条件3为任意 t ∈ T t∈T tT,都有 f ( t ) &lt; N f(t) &lt; N f(t)<N,其概率记做 p 3 p_3 p3
其中条件2和3互逆,所以有 p = p 1 ∗ ( 1 − p 3 ) = p 1 − p 1 ∗ p 3 p = p_1 * (1 - p_3) = p_1 - p_1 * p_3 p=p1(1p3)=p1p1p3
那么现在就只用求 p r e [ i ] [ j ] pre[i][j] pre[i][j],即第 i i i人做题数量小于 j j j的概率
最后答案就是 p 1 − p 1 ∗ p 3 = ∏ i = 1 T ( 1 − p r e [ i ] [ 0 ] ) − ∏ i = 1 T ( p r e [ i ] [ N − 1 ] − p r e [ i ] [ 0 ] ) p_1 - p_1 * p_3 = \prod_{i=1}^{T} (1 - pre[i][0]) - \prod_{i = 1}^{T}(pre[i][N - 1] - pre[i][0]) p1p1p3=i=1T(1pre[i][0])i=1T(pre[i][N1]pre[i][0])
而在求答案的过程中,要先求出第 i i i个人写出 j j j题的概率才能求出 p r e [ i ] [ j ] pre[i][j] pre[i][j]
f [ j ] [ k ] f[j][k] f[j][k]为第 i i i个人在写第 j j j题,要做出 k k k题的概率,其中 f [ 1 ] [ 0 ] = 1 − p [ i ] [ j ] f[1][0] = 1 - p[i][j] f[1][0]=1p[i][j], f [ 1 ] [ 1 ] = p [ i ] [ j ] f[1][1] = p[i][j] f[1][1]=p[i][j]
转移方程为
j &gt; 1 时 , f [ j ] [ k ] = 当 前 这 题 做 出 来 + 当 前 这 题 没 做 出 来 = p [ i ] [ j ] ∗ f [ j − 1 ] [ k − 1 ] + ( 1.0 − p [ i ] [ j ] ) ∗ f [ j − 1 ] [ k ] j &gt; 1时, f[j][k] = 当前这题做出来 + 当前这题没做出来 = p[i][j] * f[j - 1][k - 1] + (1.0 - p[i][j]) * f[j - 1][k] j>1,f[j][k]=+=p[i][j]f[j1][k1]+(1.0p[i][j])f[j1][k]
j = 0 时 , f [ j ] [ k ] = 当 前 这 题 没 做 出 来 = ( 1.0 − p [ i ] [ j ] ) ∗ f [ j − 1 ] [ k ] j = 0时, f[j][k] = 当前这题没做出来 = (1.0 - p[i][j]) * f[j - 1][k] j=0,f[j][k]==(1.0p[i][j])f[j1][k]
代码:

const int MAX = 1e3 + 10;
int M, T, N;
double f[MAX][MAX];//当前做第k题,做前i题能写出j题概率
double pre[MAX];//第i人写出题目数量小于等于j题概率

int main() {
	while (~scanf("%d%d%d", &M, &T, &N)) {//题数,人数,最多题数最小值
		if (M == 0 && N == 0 && T == 0)break;
		double p1 = 1.0, p2 = 1.0;
		for (int i = 1; i <= T; i++) {//第i人
			double p;
			scanf("%lf", &p);
			f[1][0] = 1.0 - p, f[1][1] = p;
			for (int j = 2; j <= M; j++) {//当前第j题
				scanf("%lf", &p);
				f[j][0] = (1.0 - p) * f[j - 1][0];
				for (int k = 1; k <= j; k++)//要做k题
					f[j][k] = p * f[j - 1][k - 1] + (1.0 - p) * f[j - 1][k];
			}
			
			pre[0] = f[M][0];//一题没做出来
			for (int j = 1; j <= N - 1; j++)
				pre[j] = pre[j - 1] + f[M][j];
			p1 *= 1.0 - pre[0];//至少做了一题
			p2 *= pre[N - 1] - pre[0];//做题数量在[1, N - 1]
		}
		printf("%.3lf\n", p1 - p2);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值