HDU-4336 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;     

}

你可能感兴趣的:(Collector)