http://acm.hdu.edu.cn/showproblem.php?pid=4336
题意:
给出n种不同卡片在买的小吃力里面出现的可能,求凑齐n种卡片要买的小吃的平均数量。
思路:
根据官方解题报告做的:
设卡片的分布p=(p1,p2,...,pn),T(p)表示拿到所有卡片时买的零食数目,有
由容斥原理得,
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 22;
double p[maxn];
int main() {
int n,i,j;
while (~scanf("%d",&n))
{
for (i = 0; i < n; ++i) scanf("%lf",&p[i]);
double ans = 0.0;
//根据二项式定理C(n,0)+C(n,1) + ... + C(n,n) = 2^n
//所以这里2^n - 1种可能,枚举
for (i = 1; i < (1<<n); ++i)
{
int ct = 0;
double tmp = 0.0;
for (j = 0; j < n; ++j)
{
if (i&(1<<j))//检查0到n中存在于i状态的点
{
ct++;
tmp += p[j];
}
}
//鸽巢定理
if (ct&1) ans += 1.0/tmp;
else ans -= 1.0/tmp;
}
printf("%.4lf\n",ans);
}
return 0;
}
状态dp:
用一个状态表示当前抽到的卡片的状况,1代表尚未拿的卡片,有d[now]=x*d[now]+sigma(si*pi*(now^(1<<i)))+1,si表示stat中该位是0还是1,x表示停留在该状态的概率,即没拿到其它卡片的概率(没抽到卡片+抽到当前已有卡片),移项可以得到d[now]=sigma(..)/(1-x)。
#include <string.h>
#include <stdio.h>
int n;
double p[25],d[1<<21];
//d[stat] stat中为1的位表示尚未拿的卡片
//d[now]=x*d[now]+sigma(si*pi*(now^(1<<i)))+1,si表示stat中该位是0还是1
//移项有d[now]=sigma(..)/(1-x) x表示停留在该状态的概率,即没拿到其它卡片
int main(){
while(scanf("%d",&n)!=EOF){
double tot=0;
for(int i=0;i<n;i++){
scanf("%lf",&p[i]);
tot+=p[i];
}
tot=1-tot,d[0]=0;
for(int i=1;i<(1<<n);i++){
double x=0,sigma=1;
for(int j=0;j<n;j++){
if(((i>>j)&1)==0)x+=p[j];
else sigma+=p[j]*d[i^(1<<j)];
}
d[i]=sigma/(1-tot-x);
}
printf("%.5f\n",d[(1<<n)-1]);
}
return 0;
}