Card Collector
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4622 Accepted Submission(s): 2340
Special Judge
Problem Description
In your childhood, do you crazy for collecting the beautiful cards in the snacks? They said that, for example, if you collect all the 108 people in the famous novel Water Margin, you will win an amazing award.
As a smart boy, you notice that to win the award, you must buy much more snacks than it seems to be. To convince your friends not to waste money any more, you should find the expected number of snacks one should buy to collect a full suit of cards.
Input
The first line of each test case contains one integer N (1 <= N <= 20), indicating the number of different cards you need the collect. The second line contains N numbers p1, p2, …, pN, (p1 + p2 + … + pN <= 1), indicating the possibility of each card to appear in a bag of snacks.
Note there is at most one card in a bag of snacks. And it is possible that there is nothing in the bag.
Output
Output one number for each test case, indicating the expected number of bags to buy to collect all the N different cards.
You will get accepted if the difference between your answer and the standard answer is no more that 10^-4.
Sample Input
1
0.1
2
0.1 0.4
Sample Output
10.000
10.500
这道题稍微要复杂一点, 不过也很简单.
题目大意就是说有n张卡, 你每次可以买一个卡包, 这个卡包可能会有一张卡或者没有卡, 每张卡都有一定几率被抽到,问将n张卡集全的期望买包次数.
n只有20…范围一看就想到状压. 用dp[i]来表示i这种状态还需买包的期望次数. i就是拆成二进制下的状态, 第i位是1就说明第i张卡已被抽到, 是0就没有.
仍然选择倒着来, 那么dp[(1 << n)-1] = 0, 因为此时二进制下每位都是1, 代表卡牌都选了, 那么就不需要买包了. 对于当前i状态, 如果买了一个卡包, 出来的卡是已收集之中的, 那么就是dp[i]转移到dp[i], 买了一包没有卡也是dp[i] 转移到 dp[i]. 对于其他情况则是转移到 dp[i | (1 << j)] (卡包抽到第j张卡).
没抽到的几率就是1-所有卡的被抽到的概率之和.
那么由期望的线性转移方程就是
dp[i] = dp[i] * x + dp[i | (1 << j] * p[j]… + 1
(x为抽到已有的或者没抽到的概率之和).
移项之后变成 dp[i] = (dp[i | (1 << j)]… + 1) / (1 - x);
dp[0]就是答案.
#include<stdio.h>
const int maxn = 21;
int lim, n;
double sum, no, none, p[maxn], dp[1 << maxn];
int main(){
while(scanf("%d", &n) != EOF){
none = 1.0, lim = (1<<n) - 2;
for(int i = 0; i < n; ++i) scanf("%lf", &p[i]), none -= p[i];
for(int i = lim; ~i; --i){
sum = 1, no = 0;
for(int j = 0; j < n; ++j)
if(i & (1<<j)) no += p[j];
else sum += dp[i | (1<<j)] * p[j];
dp[i] = sum / (1 - none - no);
}
printf("%.5f\n", dp[0]);
}
}