题目链接:http://acm.fzu.edu.cn/problem.php?pid=2103
题意:n个面的色子,出现第i个面的概率为pi。将该色子掷K次,给出K次中前r大的数字。求这种情况出现的概率。
思路:设f[i][j]表示i个数字最大数字为j的概率。这个很容易得到。设Min为前r大的数字中最小的数字。接着就是枚举K-r中Min出现了多少次,设为t。这样我们就能计算出r+t个数字的概率,就是排列组合的问题,再乘以f[K-r-t][Min-1]即可。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <cmath>
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int C;
int n,K,r,a[30],b[30],aNum;
double f[55][55],p[25],c[55],cc[55][55];
void init()
{
c[0]=1;
int i,j;
for(i=1;i<55;i++) c[i]=c[i-1]*i;
for(i=1;i<=52;i++)
{
cc[i][0]=cc[i][i]=1;
for(j=1;j<i;j++) cc[i][j]=cc[i-1][j]+cc[i-1][j-1];
}
}
void cal(int x,int t)
{
int i,j,k;
memset(f,0,sizeof(f));
for(i=0;i<=t;i++) f[0][i]=1,f[1][i]=p[i];
for(i=1;i<x;i++)
{
for(j=1;j<=t;j++) for(k=1;k<=t;k++)
{
f[i+1][max(j,k)]+=f[i][j]*p[k];
}
}
for(i=1;i<=x;i++)
{
f[i][0]=0;
for(j=1;j<=t;j++) f[i][j]+=f[i][j-1];
}
}
void Add(int x)
{
int i;
for(i=1;i<=aNum;i++) if(a[i]==x)
{
b[i]++;
return;
}
aNum++;
a[aNum]=x;
b[aNum]=1;
}
double cal1(int x)
{
double ans=log(c[r+x]);
int i;
for(i=1;i<=aNum;i++) ans-=log(c[b[i]]);
return ans;
}
int main()
{
init();
for(scanf("%d",&C);C--;)
{
scanf("%d%d%d",&n,&K,&r);
int i,j;
for(i=1;i<=n;i++) scanf("%lf",&p[i]);
double temp=0;
int Min=n;
aNum=0;
for(i=1;i<=r;i++)
{
scanf("%d",&j);
temp+=log(p[j]);
if(j<Min) Min=j;
Add(j);
}
cal(K-r,Min);
double ans=0;
for(i=0;i<=K-r;i++)
{
ans+=exp(log(cc[K][r+i])+temp+cal1(i)+log(f[K-r-i][Min-1]));
Add(Min);
temp+=log(p[Min]);
}
printf("%.6lf\n",ans);
}
return 0;
}