题目大意:有一个大小为k的缓存区,每次从n种物品中按照一定的概率选取一种物品尝试放进去.同一个物品每一次选取的概率都是相同的.如果这种物品已经放进去过就不再放进去.如果缓存区满了就把放进去的时间离现在最远的物品拿出来.问10^100次后每个物品在缓冲区中的概率.
题解:概率与期望+状压DP
如果是正着考虑,那么我们会出现很多废弃的状态。而且还会出现进去的被弹出来的情况,十分不好想。那么我们倒着考虑,那么最后一次加入的一定在最终的集合中。因为10^100基本上接近无限了,那么最后的集合一定是满的。
也就是一定存在k个不同的物品。
dp[i]表示到达状态i的概率。
dp[i|(1<<j)]+=dp[i]*p[j] j不在i中。注意的是没次选择还有一定的概率选择到i集合中的数。所以dp[i]/=(1-sum) sum表示i集合中数的概率。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 21
using namespace std;
int n,k;
double ans[1<<N],f[1<<N],p[N];
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d",&n,&k); int c=0;
for (int i=0;i<n;i++) {
scanf("%lf",&p[i]);
if (p[i]!=0) c++;
}
k=min(k,c);
f[0]=1;
for (int i=0;i<(1<<n);i++) {
double cnt=0,now=0;
for (int j=0;j<n;j++)
if ((i>>j)&1) cnt++,now+=p[j];
if (cnt==k) {
for (int j=0;j<n;j++)
if ((i>>j)&1) ans[j]+=f[i];
continue;
}
if (cnt>k) continue;
if (1.0-now==0) continue;
f[i]/=(1.0-now);
for (int j=0;j<n;j++)
if (!((i>>j)&1)) f[i|(1<<j)]+=f[i]*p[j];
}
for (int i=0;i<n;i++) printf("%.12lf%c",ans[i]," \n"[i==n-1]);
}