题意:给你你的n个小伙伴一人给你带了一种类型的礼物,告诉你各个礼物的数量,和每个小伙伴给哪种礼物的概率,问你选择哪种类型的礼物最有可能是第一个人给的,这个概率是多少。
思路:设A为第一个小伙伴给某种礼物的概率,B为所有人给的礼物恰好是给定的数目的概率P(A|B)=P(AB)/P(B)。B的概率由dp递推得出。最后还得除以这个礼物的数目,再找到最大的这个概率。
AC代码如下:
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
double dp[100010],p[13][5],eps=1e-10;
ll point[100010],S_num[100010],S_pos,pow13[10];
map<ll,int> match;
int T,t,n,num[5];
int dcmp(double x)
{
return (x>eps)-(x<-eps);
}
bool check(ll S,int pos)
{
ll ret=S%pow13[pos+1]/pow13[pos];
if(ret+1<=num[pos])
return true;
else
return false;
}
int get(ll S,int ret)
{
int k=match[S];
if(k>0)
return k;
else
{
match[S]=++S_pos;
point[S_pos]=S;
S_num[S_pos]=ret;
dp[S_pos]=0;
return S_pos;
}
}
void solve(ll S,int pos)
{
int i,j,k1,k2,ret;
k1=match[S];
ret=S_num[k1]+1;
k2=get(S+pow13[pos],ret);
dp[k2]+=dp[k1]*p[ret][pos];
//printf("%d %d %lf %lf %lf\n",k1,k2,dp[k2],dp[k1],p[ret][pos]);
}
int main()
{
int i,j,k,pos;
double A,B,MAXN,p2;
ll MAXS,S;
pow13[0]=1;
for(i=1;i<=6;i++)
pow13[i]=pow13[i-1]*13;
scanf("%d",&T);
for(t=1;t<=T;t++)
{
match.clear();
scanf("%d",&n);
for(i=0;i<5;i++)
scanf("%d",&num[i]);
for(i=0;i<n;i++)
for(j=0;j<5;j++)
scanf("%lf",&p[i][j]);
for(j=0;j<5;j++)
p[n][j]=p[0][j];
point[1]=0;
S_num[1]=0;
S_pos=1;
match[0]=1;
dp[1]=1;
//printf("%.3f\n",dp[1]);
for(i=1;i<=S_pos;i++)
for(j=0;j<5;j++)
if(check(point[i],j))
solve(point[i],j);
for(i=1;i<=S_pos;i++)
{
//printf("%d %lld %lld %.3f\n",i,point[i],S_num[i],dp[i]);
}
B=dp[S_pos];
MAXN=0;
MAXS=0;
for(i=0;i<=4;i++)
MAXS+=pow13[i]*num[i];
for(i=0;i<=4;i++)
if(num[i]>0)
{
S=MAXS-pow13[i];
k=match[S];
p2=p[0][i]*dp[k]/B/num[i];
if(dcmp(p2-MAXN)>0)
{
MAXN=p2;
pos=i;
}
}
printf("%d %.3f\n",pos+1,MAXN);
}
}