HDU4336 Card Collector 容斥定理 Or 概率DP

首先这题可以用期望DP来计算最后的期望值,由于这题每张卡片对应的概率是不相同的,所以不能像POJ-2096那样dp[i]表示拿到了i 张卡片来表示状态,而是要开一个 1<<N的状态来压缩状态表示拿到不用的卡片的期望值。对于给定的N,我们有dp[(1<<N)-1]=0,因为这已经是最后的状态了。

对于dp[i] 我们需要分析其能够到达的状态,如果 N=6, i 的二进制位为 011011,那么可能买零食不改变原来状态,也就是中了已经有了的卡片或者是没有中卡片,所以到达原来状态的概率是p[1]+p[2]+p[4]+p[5]+NONE,这一项是要移到待会儿方程的左边去解的,因为dp[i]还未知,不能够直接带入求解。那么 i 能够到达哪些状态呢?这里就是凡是为0的位都可以到达,前面写了个(1<<bit)^i 来判定是否为零错了,应该还是要 !((1<<bit) & i) 来判定靠谱...... 转移到下一个状态的概率就非常好算了,直接乘以该位对应的概率就可以了。

代码如下:

#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;

int N;

double seq[25], dp[1100000], p[1100000];

void pre()
{
    int LIM = 1 << N;
    for (int i = 0; i < LIM; ++i) {
        p[i] = 0;
        for (int j = 0; j < N; ++j) {
            if ((1 << j) & i) {  // 低位对应编号小的概率 
                p[i] += seq[j+1];
            }    
        }
    }
}

int main()
{
    int temp;
    double none, tot, myself;
    while (scanf("%d", &N) == 1) {
        dp[(1<<N)-1] = none = 0;
        for (int i = 1; i <= N; ++i) {
            scanf("%lf", &seq[i]);
            none += seq[i];
        }
        none = 1 - none;
        pre();
        for (int i = (1<<N)-2; i >= 0; --i) {
            tot = 0;
            myself = p[i] + none; 
            for (int j = 0; j < N; ++j) {
                if (!((1 << j)&i)) { 
                    temp = i | (1 << j);
                    tot += seq[j+1] * dp[temp];
                }
            }
            dp[i] = (tot + 1) / (1 - myself);
        }
        printf("%.6lf\n", dp[0]);
    }
    return 0;
}

这题真心不明白容斥定理是怎么来的。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;

int N;

double seq[25];

int main()
{
    int mask, odd;
    double sum, ret;
    while (scanf("%d", &N) == 1) {
        ret = 0;
        for (int i = 1; i <= N; ++i) {
            scanf("%lf", &seq[i]);
        }
        mask = 1<<N;
        for (int i = 1; i < mask; ++i) {
            odd = 0;
            sum =0;
            for (int j = 0; j < N; ++j) {
                if ((1 << j) & i) {
                    ++odd;
                    sum += seq[j+1];
                }
            }
            if (odd & 1) {  // 加奇减偶 
                ret += 1./sum;
            }
            else {
                ret -= 1./sum;        
            }
        }
        printf("%.6lf\n", ret);
    }
    return 0;     
}
原文地址:https://www.cnblogs.com/Lyush/p/2623439.html