HDU 4336 概率DP求期望(or容斥原理)

题意:
    有N(1<=N<=20)张卡片,每包中含有这些卡片的概率为p1,p2,````pN.
    每包至多一张卡片,可能没有卡片。
    求需要买多少包才能拿到所以的N张卡片,求次数的期望。
    
    可以用状态压缩dp来求概率,做过基本的概率dp后应该没什么问题
    这里我主要说说容斥原理的解法。

    如果一个事件发生的概率为p, 那么它第一次发生时的次数期望就是1/p

    1:     1/p 
    2:    (1-p)*p
    3:    (1-p)^2*p
    ......
    n:    (1-p)^(n-1)*p

    以上求和,用错位相消的方法求出 E = 1/p
     
    类似的,我们可以得出 如果两个事件发生的概率分别为p1,p2, 那么
    第一次发生其中的某一件的次数期望就是1/(p1+p2)。

    根据这个结论我们就可以用容斥原理来做这题了。
容斥:
#include 
#include 
#include 
using namespace std;
int n, two[22];
double p[22], ans;
int main() {
	int i, j;
	two[0] = 1;
	for(i = 1; i <= 20; i++)
		two[i] = two[i-1] << 1;
	while( ~scanf("%d", &n)) {
		for(i = 0; i < n; i++)
			scanf("%lf", &p[i]);
		ans = 0;
		for(i = 1; i < two[n]; i++) {
			double sum = 0;
			int c = 0;
			for(j = 0; j < n; j++)
				if(i&two[j]) {
					sum += p[j];
					c++;
				}
			if(c&1) ans +=  1/sum;
			else ans -= 1/sum;
		}
		printf("%f\n", ans);
	}
	return 0;
}


状态压缩:
#include 
#include 
#include 
using namespace std;
double p[22], dp[1<<20], p0;
int two[22];
int n;
double tp, ans;
int main() {
    int i, j;
    two[0] = 1;
    for(i = 1; i <= 20; i++)
        two[i] = two[i-1] << 1;
    while( ~scanf("%d", &n) ) {
        p0 =  0;
        for(i = 0; i < n; i++) {
            scanf("%lf", &p[i]);
            p0 += p[i];
        }
        p0 = 1-p0;
        dp[two[n]-1] = 0;
        for(i = two[n]-2; i >= 0; i--) {
            tp = p0; ans = 1;
            for(j = 0; j < n; j++)
                if(two[j]&i) tp += p[j];
                else ans += p[j]*dp[two[j]|i];
            dp[i] = ans/(1-tp);
        }
        printf("%.5f\n", dp[0]);
    }
    return 0;
}


你可能感兴趣的:(ACM_数论,ACM_DP_概率DP)