hdu 4336 概率dp + 状压
小吃包装袋里面有随机赠送一些有趣的卡片,现在你想收集齐 N 张卡片,每张卡片在食品包装袋里出现的概率是p[i] ( Σp[i] <= 1 ), 问你收集所有卡片所需购买的食品数量的期望是多少。
对于每袋食品,有两种结果,该卡片已经收集到了和没有收集到(没有卡片的情况视为收集到了)。
把已经收集到的卡片的集合记为 s ,dp[s] 表示已经收集到集合s的卡片情况下收集齐所有的卡片的购买数量的期望,s 为空集即为所求。s 为全集时dp[s] = 0;
对于上面说的两种情况 _si 表示集合 s 添加一个不在 s 中的卡片 i 的集合 Σ((dp[_si] + 1) * p[i]) ,而抽到已经收集到的卡片则是dp[s];
dp[s] = Σ((dp[_si] + 1) * p[i]) + dp[s] * (1 - Σ(p[i]));
而集合s我们可以用二进制很好解决。
/*********************************************** ** problem ID : hdu_4336.cpp ** create time : Fri Jul 24 20:41:26 2015 ** auther name : xuelanghu ** auther blog : blog.csdn.net/xuelanghu407 **********************************************/ #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n; double p[22]; double dp[(1<<20) + 5]; double DP(int t) { if (dp[t] != -1.0) return dp[t]; if (t == (1 << n) - 1) return dp[t] = 0.0; double rec = 0.0; double _p = 0.0; for (int i=0; i<n; i++) { if ((t >> i & 1) == 0) { rec += p[i] * DP(t | (1 << i)); _p += p[i]; } } return dp[t] = (rec + 1.0) / _p; } int main () { while (scanf("%d", &n) != EOF) { for (int s = 0; s < (1 << n)-1; s++) { dp[s] = -1.0; } for (int i=0; i<n; i++) { scanf("%lf", &p[i]); } printf ("%.6lf\n",DP(0)); } return 0; }
求期望的dp从后往前推的原因大概是目标状态的期望是可以直接求出来的(一般是0啊什么的)。