【概率dp】D. Card Collector

https://www.bnuoj.com/v3/contest_show.php?cid=9147#problem/D

【题意】

为了集齐n张卡片,必须要买多少袋零食?题目给定每种卡片出现在零食中的概率。

【思路】

以2张卡片为例,dp[00]表示要从00->11需要的零食数,则初始化dp[11]=0,dp[00]就是我们要求的。

那么在dp[00]这个状态,我们需要买一袋零食。

如果这袋零食有卡片1,那么后续我们还要操作dp[01];

如果这袋零食有卡片2,那么后续我们还要操作dp[10];

如果这袋零食没有卡片,那么后续我们还要操作dp[00]。

所以dp[00]=(1+p[0]*dp[01]+p[1]*dp[10]+(1-p[0]-p[1])*dp[00])

由此类推

dp[0...0]=(1+sgm(p[k]*dp[i|1<<k])+(1-sgm(p[k]))*dp[0.....0])

题目给的n最多是20,所以可以用状压dp

常用的状压dp技巧:

i&(1<<k):看i的第k位是0还是1

if(!(i&(1<<k))) i|=(1<<k)

把i的第k位从0变成1

尤其要注意<<的优先级。

【Accepted】

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<string>
 6 #include<cstring>
 7 using namespace std;
 8 int n;
 9 const int maxn=25;
10 const int inf=0x3f3f3f3f;
11 double a[maxn];
12 double dp[1<<21];
13 int main()
14 {
15     while(~scanf("%d",&n))
16     {
17         memset(dp,0,sizeof(dp));
18         for(int i=0;i<n;i++)
19         {
20             scanf("%lf",&a[i]);    
21         }
22         dp[(1<<n)-1]=0.0;
23         for(int i=(1<<n)-2;i>=0;i--)
24         {
25             double phi=0.0;
26             for(int k=0;k<n;k++)
27             {
28                 if((i&(1<<k)))
29                 {
30                     continue;
31                 }
32                 dp[i]+=a[k]*dp[i|(1<<k)];
33                 phi+=a[k];
34             }
35             dp[i]=(dp[i]+1.0)/phi;
36         }
37         printf("%.6lf\n",dp[0]);
38         
39     }
40     return 0;
41 }
View Code

 

posted @ 2017-07-04 21:45  shulin15  阅读(177)  评论(0编辑  收藏  举报