【概率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 }