Description
n个视频,长度为k的缓存,每次询问,每个视频以pi的概率被选,如果不在缓存区则加入,如果缓存区满了,则最先进缓存的出来,问10^100次操作以后每个视频在缓存的概率
Input
第一行两个整数n和k,第二行n个数表示每个视频被选中的概率
(1<=k<=n<=20,0<=pi<=1,sum(pi)=1)
Output
输出10^100次操作后每个视频在缓存中出现的概率
Sample Input
3 1
0.3 0.2 0.5
Sample Output
0.3 0.2 0.5
Solution
倒过来想这些操作,那么问题变成了,如果第i个视频没有出现在缓存中,那么以概率pi被选中放在缓存中,当缓存中视频数量为k时停止,问每个视频出现在缓存中的概率。用一个20位的二进制数表示一个状态,二进制位为1表示对应视频出现在缓存中,为0则没有出现,若某一个状态mark&(1 << i)=0,说明第i个视频在当前状态还没有在缓存中,那么就可以转移到mark|(1 << i)这个状态,转移方程是dp[mark|( 1<< i)]+=dp[mark]*p[i]/sum,其中sum是没有出现在缓存中视频出现概率之和,那么每个视频出现在缓存中的概率就是将所有二进制1个数为k,且该视频对应二进制位为1的状态dp值累加即可
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 22
#define eps 1e-6
int k,n;
double p[maxn],dp[(1<<20)],ans[maxn];
int deal(int x)
{
int ans=0;
for(int i=0;i<n;i++)
if(x&(1<<i))ans++;
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
int cnt=0;
memset(dp,0,sizeof(dp));
memset(ans,0,sizeof(ans));
dp[0]=1;
for(int i=0;i<n;i++)
{
scanf("%lf",&p[i]);
if(p[i]<eps)cnt++;
}
k=min(k,n-cnt);
for(int i=0;i<(1<<n);i++)
{
double temp=1;
for(int j=0;j<n;j++)
if(i&(1<<j))
{
temp-=p[j];
if(deal(i)==k)ans[j]+=dp[i];
}
for(int j=0;j<n;j++)
if((i&(1<<j))==0)
dp[i|(1<<j)]+=dp[i]*p[j]/temp;
}
for(int i=0;i<n;i++)
printf("%.7lf%c",ans[i],i==n-1?'\n':' ');
}
return 0;
}